/* Admin console — same tokens as the public site, no new palette.
   Imported rather than inlined: unlike the event page (a one-shot campaign
   landing page opened from Instagram), this is a repeat-visit tool where the
   browser cache is worth more than saving one request. */

@import url("/tokens.css");

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The UA stylesheet says [hidden] { display: none }, but that loses to ANY
   class rule that sets display — .gate below sets display:flex, so the login
   gate painted on top of the signed-in app: heading, "sign in with Google",
   and an empty button container, because GIS is never loaded once a session
   exists. It read as "the Google button disappeared" and produced no console
   error, because nothing was wrong in JS.
   Every view in this console is toggled with the hidden attribute, so this
   guard has to be global and has to win. */
[hidden] { display: none !important; }

body {
  font-family: var(--font-body);
  background: var(--albi-cream);
  color: var(--text-body);
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

button, input, select, textarea { font-family: inherit; font-size: 15px; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-variation-settings: var(--display-bold);
  font-weight: 640;
  color: var(--albi-charcoal);
}

h1 { font-size: 30px; }
h2 { font-size: 24px; }
h3 { font-size: 18px; }

/* ── login gate ───────────────────────────────────────────────────────────── */

.gate {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 24px;
  text-align: center;
}

.gate .mark { height: 44px; width: auto; margin-bottom: 6px; }
.gate .sub { color: var(--albi-charcoal-70); font-size: 15px; }
.gate .err { color: var(--albi-orange-deep); font-size: 14px; font-weight: 600; max-width: 380px; }

/* ── chrome ───────────────────────────────────────────────────────────────── */

.bar {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 14px 20px;
  background: var(--albi-cream-soft);
  border-bottom: 1px solid var(--albi-cream-deep);
  position: sticky;
  top: 0;
  z-index: 10;
  flex-wrap: wrap;
}

.bar > img { height: 28px; width: auto; }

/* `flex-wrap` and `min-width: 0` together are what stop the header pushing the
   PAGE wider than the window. A flex item defaults to `min-width: auto`, so the
   nav refused to shrink below the width of its buttons laid out in one line;
   with seven tabs on a laptop it overflowed the bar, clipped the last tab, and
   put a horizontal scrollbar under the whole console. It wraps now, and it is
   allowed to be narrower than its contents want to be. */
.bar nav { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }

.bar nav button {
  border: 0;
  background: transparent;
  color: var(--albi-charcoal-70);
  padding: 8px 14px;
  border-radius: 999px;
  cursor: pointer;
  min-height: 40px;
}

.bar nav button.on { background: var(--albi-charcoal); color: var(--albi-cream); }

/* ── the account menu ───────────────────────────────────────────────────────
   The avatar sits at the far end of the bar. In an RTL document that is the
   LEFT corner, which is why this is `margin-inline-start: auto` and not a
   `left` — the logical property puts it in the right place in both directions
   without a second rule. `position: relative` is the anchor the dropdown hangs
   from. */

.bar .who { margin-inline-start: auto; position: relative; display: flex; align-items: center; }

/* 34px to match the logo beside it (.bar > img is height: 28px plus its optical
   padding), so the two read as one row of chrome rather than a badge stuck on
   the end. */
.avatar-btn {
  inline-size: 34px;
  block-size: 34px;
  padding: 0;
  border: 1.5px solid var(--albi-cream-deep);
  border-radius: 999px;
  background: var(--albi-charcoal);
  color: var(--albi-cream);
  cursor: pointer;
  overflow: hidden;
  display: grid;
  place-items: center;
  flex: none;
  transition: border-color var(--dur-mid, 160ms) var(--ease-out-soft, ease);
}

.avatar-btn:hover { border-color: var(--albi-orange); }
.avatar-btn:focus-visible { outline: 2px solid var(--albi-orange); outline-offset: 2px; }

/* grid-area pins the photo into the same cell as the initials below it, so one
   covers the other instead of stacking. */
.avatar-btn img {
  grid-area: 1 / 1;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  display: block;
}

/* Sits UNDER the photo in the same grid cell, so it is already painted when the
   image arrives and there is no flash of empty circle. */
.avatar-initials {
  grid-area: 1 / 1;
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1;
  user-select: none;
}

.account-menu {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  min-inline-size: 210px;
  padding: 6px;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md, 14px);
  box-shadow: var(--shadow-lg, 0 16px 40px rgb(76 73 69 / 16%));
  z-index: 20;
  animation: account-in 120ms var(--ease-out-soft, ease) both;
}

@keyframes account-in { from { opacity: 0; transform: translateY(-4px); } }

.account-who {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 10px 10px;
  border-block-end: 1px solid var(--albi-cream-deep);
  margin-block-end: 4px;
}

.account-who strong { font-size: 14px; color: var(--albi-charcoal); }
.account-who span { font-size: 12px; color: var(--albi-charcoal-70); }

.account-menu [role="menuitem"] {
  display: block;
  inline-size: 100%;
  text-align: start;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14px;
  color: var(--albi-charcoal);
  padding: 9px 10px;
  border-radius: var(--radius-sm, 9px);
  cursor: pointer;
  min-block-size: 40px;
}

.account-menu [role="menuitem"]:hover { background: var(--albi-cream); }
.account-menu [role="menuitem"]:focus-visible { outline: 2px solid var(--albi-orange); outline-offset: -2px; }

@media (prefers-reduced-motion: reduce) {
  .account-menu { animation: none; }
}

main { max-width: 1040px; margin: 0 auto; padding: 26px 20px 80px; }

.head { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; flex-wrap: wrap; }
.head.sub2 { margin-top: 34px; }

/* The auto margin pushes ONE thing to the far end. With two buttons in a head
   every one of them got it, which spread them across the row instead of
   grouping them — hence .acts: the group takes the margin, its children don't. */
.head > a, .head > button, .head > .acts { margin-inline-start: auto; }
.head > .acts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.head > .acts > * { margin-inline-start: 0; }
.head > .pill { margin-bottom: 0; }
.hint { font-size: 13.5px; color: var(--albi-charcoal-70); margin: -6px 0 12px; }

.back {
  border: 0;
  background: transparent;
  color: var(--albi-charcoal-70);
  cursor: pointer;
  padding: 6px 0;
  margin-bottom: 8px;
  min-height: 40px;
}

.back:hover { color: var(--albi-orange); }

/* ── buttons ──────────────────────────────────────────────────────────────── */

.primary {
  background: var(--albi-orange);
  color: var(--text-on-accent-strong);
  border: 0;
  border-radius: 12px;
  padding: 11px 20px;
  cursor: pointer;
  font-weight: 700;
  min-height: 44px;
}

.primary:hover { background: var(--albi-orange-deep); }

.ghost {
  border: 1.5px solid var(--albi-charcoal-40);
  background: transparent;
  color: var(--albi-charcoal);
  border-radius: 12px;
  padding: 10px 18px;
  cursor: pointer;
  text-decoration: none;
  display: inline-block;
  min-height: 44px;
}

.ghost:hover { border-color: var(--albi-orange); color: var(--albi-orange); }
.sm { padding: 7px 14px; font-size: 13.5px; min-height: 38px; }

/* ── cards ────────────────────────────────────────────────────────────────── */

.cards { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

.card {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-lg, 22px);
  padding: 18px;
  cursor: pointer;
  transition: transform .14s, box-shadow .14s;
}

.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg, 0 16px 40px rgba(76, 73, 69, .16)); }
.card h3 { margin-bottom: 4px; }
.card .when { font-size: 13.5px; color: var(--albi-charcoal-70); }
.card .row2 { display: flex; gap: 14px; margin-top: 12px; font-size: 13.5px; }
.card .row2 b { font-size: 20px; display: block; color: var(--albi-charcoal); }

.pill {
  display: inline-block;
  font-size: 11.5px;
  letter-spacing: .12em;
  padding: 3px 10px;
  border-radius: 999px;
  margin-bottom: 8px;
}

.pill.live { background: var(--status-positive); color: var(--albi-cream); }
.pill.draft { background: var(--albi-charcoal-40); color: var(--albi-charcoal); }
.pill.closed, .pill.archived { background: var(--albi-charcoal); color: var(--albi-cream); }

/* ── stats ────────────────────────────────────────────────────────────────── */

/* 150px, not 120px, and the number shrinks at 3+ characters.
   Measured in the browser: "100%" in the display font at 28px is 141px wide.
   At the old 120px track the content box was 99px on desktop and ~72px on a
   390px phone (three columns), so the conversion tile painted its value
   straight out of its own box and over the neighbouring one. No font size
   rescues three columns on a phone — 100% at 20px is still 101px — so the
   track has to widen too. 150px gives two columns on a phone (content ~131px)
   and .wide brings "100%" down to 111px, which fits with room to spare. */
.stats { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }

.stat {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: 16px;
  padding: 14px 16px;

  /* Grid items default to min-width:auto, which lets content force a track
     wider than its own cell instead of being constrained by it. */
  min-width: 0;
}

.stat b {
  display: block;
  font-family: var(--font-display);
  font-variation-settings: var(--display-bold);
  font-weight: 640;
  font-size: 28px;
  color: var(--albi-charcoal);
  line-height: 1.1;

  /* Never let a value wrap mid-number; it is a figure, not prose. */
  white-space: nowrap;
}

/* Set by loadStats() for values of 3+ characters — in practice the conversion
   percentage, the only tile whose value is not a bare count. */
.stat b.wide { font-size: 22px; }

.stat span { font-size: 12.5px; color: var(--albi-charcoal-70); }

/* ── links ────────────────────────────────────────────────────────────────── */

.links { display: grid; gap: 10px; }

.link {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: 16px;
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.link .nm { font-weight: 600; color: var(--albi-charcoal); display: flex; align-items: center; gap: 8px; }
.link .url { font-size: 12.5px; color: var(--albi-charcoal-70); direction: ltr; unicode-bidi: isolate; }
.link .num { text-align: center; min-width: 62px; }
.link .num b { display: block; font-size: 19px; color: var(--albi-charcoal); }
.link .num span { font-size: 11.5px; color: var(--albi-charcoal-70); }
.link .acts { margin-inline-start: auto; display: flex; gap: 8px; flex-wrap: wrap; }

/* ── tables ───────────────────────────────────────────────────────────────── */

.table { overflow-x: auto; border: 1px solid var(--albi-cream-deep); border-radius: 16px; background: var(--albi-paper); }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: start; padding: 11px 14px; border-bottom: 1px solid var(--albi-cream-deep); white-space: nowrap; }
th { background: var(--albi-cream-soft); font-weight: 600; color: var(--albi-charcoal); font-size: 13px; }
tr:last-child td { border-bottom: 0; }
td.ltr { direction: ltr; unicode-bidi: isolate; text-align: start; }
.empty { padding: 30px; text-align: center; color: var(--albi-charcoal-70); }

/* ── channel badge ────────────────────────────────────────────────────────── */

/* The share-link channel, shown as a logo next to its name in three places: the
   new-link picker, the links list, and the "מקור" column. One size everywhere —
   the badge is a label, not a feature, and it should never outweigh the name it
   sits beside.

   `flex: none` matters: inside a flex row an <img> is a flex item and will be
   squashed by a long link name (they are user-entered and can be long), which
   would show the logo progressively more distorted the longer the name. */
.ch { flex: none; width: 22px; height: 22px; display: block; }

/* "מקור" — icon + link name. inline-flex so the cell still shrink-wraps. */
.src { display: inline-flex; align-items: center; gap: 7px; }

/* Attendance is marked at the gate, on a phone, one-handed. It is a select in
   the row rather than a modal: one tap, saved, no navigation. */
select.st {
  border: 1.5px solid var(--albi-charcoal-40);
  border-radius: 999px;
  padding: 5px 10px;
  font-size: 13px;
  background: var(--albi-white);
  min-height: 38px;
  cursor: pointer;
}

select.st:focus { outline: none; border-color: var(--albi-orange); }
select.st.attended { border-color: var(--status-positive); color: var(--status-positive); font-weight: 700; }
select.st.cancelled, select.st.noshow { color: var(--albi-charcoal-70); }
tr.off td { opacity: .55; }

/* ── dialog ───────────────────────────────────────────────────────────────── */

dialog {
  border: 0;
  border-radius: 20px;
  padding: 24px;
  max-width: 420px;
  width: calc(100% - 32px);
  background: var(--albi-paper);
  color: var(--text-body);

  /* A modal dialog is centred by the UA with `inset: 0; margin: auto` — and the
     `* { margin: 0 }` reset at the top of this file wipes the auto out, which
     drops every dialog into the corner (measured: 1183,0 in a 1843px viewport
     — hard against the inline-start edge and the top). Putting it back is the
     whole centring mechanism; it is not a spacing tweak. */
  margin: auto;
}

dialog::backdrop { background: rgba(76, 73, 69, .45); }
dialog h3 { margin-bottom: 14px; }
dialog label { display: block; margin-bottom: 14px; font-size: 14px; color: var(--albi-charcoal); font-weight: 600; }
dialog label small { display: block; font-weight: 400; color: var(--albi-charcoal-70); font-size: 12.5px; margin-bottom: 4px; }

/* ── channel picker ───────────────────────────────────────────────────────── */

/* Five radio chips in place of a <select>, because an <option> cannot hold an
   <img> and the channel is the one field here that reads faster as a logo. */
.chan { border: 0; padding: 0; margin: 0 0 14px; }

.chan legend {
  padding: 0;
  margin-bottom: 7px;
  font-size: 14px;
  font-weight: 600;
  color: var(--albi-charcoal);
}

.chan label {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 8px;
  margin-inline-end: 8px;
  padding: 7px 13px 7px 15px;
  border: 1.5px solid var(--albi-charcoal-40);
  border-radius: 999px;
  background: var(--albi-white);
  font-size: 14px;
  font-weight: 400;
  cursor: pointer;

  /* Same 46px tap target as the inputs above it — this dialog is used on a
     phone, and a 30px chip is a miss waiting to happen. */
  min-height: 46px;
}

/* The radio itself is redundant once the chip shows its own selected state, but
   it stays in the DOM and focusable: it is what carries the value to FormData,
   what makes the group arrow-key navigable, and what screen readers announce.
   Hidden by clipping rather than display:none, which would drop it from the
   tab order and take the keyboard path with it. */
.chan input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  min-height: 0;
  opacity: 0;
  pointer-events: none;
}

.chan img { flex: none; width: 22px; height: 22px; display: block; }

.chan label:hover { border-color: var(--albi-charcoal-70); }

.chan label:has(input:checked) {
  border-color: var(--albi-orange);
  background: var(--albi-cream-soft);
  font-weight: 700;
}

/* Focus lives on the chip, since the input it belongs to is visually gone.
   Without this the group is keyboard-navigable but invisibly so. */
.chan label:has(input:focus-visible) {
  outline: 2px solid var(--albi-orange);
  outline-offset: 2px;
}

/* The unselected chips are muted so the chosen one carries the row. Kept off
   the icons themselves at full opacity would make five logos compete with the
   two fields above; :has() is safe to lean on here — the console is a modern
   evergreen-browser tool behind a Google sign-in, not a public page. */
.chan label:not(:has(input:checked)) img { opacity: .55; }

/* The event editor is ~20 fields. It gets a wider box and its own scroll, and
   the action row stays stuck to the bottom — a Save button that has scrolled
   out of sight reads as a form with no way to submit it.

   [open] is load-bearing, exactly as it is for [hidden] at the top of this file.
   The UA hides a closed dialog with `dialog:not([open]) { display: none }`, and
   ANY author rule that sets display beats it — so an unqualified
   `dialog.wide { display: flex }` painted the closed editor permanently into
   the bottom corner of the page, 660×806, over the content. Never set display
   on a dialog without qualifying it. */
dialog.wide { max-width: 660px; max-height: min(86dvh, 900px); overflow: hidden; }
dialog.wide[open] { display: flex; }
dialog.wide form { overflow-y: auto; min-height: 0; width: 100%; padding-inline-end: 4px; }
dialog.wide h4:first-of-type { border-top: 0; padding-top: 0; }

dialog.wide .row {
  flex: none;
  position: sticky;
  bottom: 0;
  background: var(--albi-paper);
  border-top: 1px solid var(--albi-cream-deep);
  padding-top: 14px;
  margin-top: 4px;
}

dialog h4 {
  font-family: var(--font-body);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--albi-charcoal-70);
  border-top: 1px solid var(--albi-cream-deep);
  padding-top: 14px;
  margin-top: 4px;
  margin-bottom: 12px;
}

dialog .grid2 { display: grid; gap: 0 14px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
dialog .err { color: var(--albi-orange-deep); font-size: 13.5px; font-weight: 600; margin-top: 8px; }

dialog input, dialog select, dialog textarea {
  width: 100%;
  margin-top: 5px;
  padding: 11px 13px;
  border: 1.5px solid var(--albi-charcoal-40);
  border-radius: 12px;
  background: var(--albi-white);
  min-height: 46px;
  font-weight: 400;
}

dialog textarea { resize: vertical; line-height: 1.5; font-size: 15px; }
dialog input:focus, dialog select:focus, dialog textarea:focus { outline: none; border-color: var(--albi-orange); }
dialog .row { display: flex; gap: 10px; justify-content: flex-end; margin-top: 18px; }

/* ── העסק · POS / business dashboard ──────────────────────────────────────────
   Same palette and radii as the rest of the console. Added as its own block so
   the events UI above is untouched; every class here is prefixed pos-/kpi-/bar-/
   prod-/emp- or trend- to keep the namespace obvious. */

.pos-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 22px;
}

/* segmented tab group + range presets — same pill vocabulary as .bar nav */
.pos-tabs, .pos-range {
  display: flex;
  gap: 4px;
  background: var(--albi-cream-soft);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-pill);
  padding: 4px;
  flex-wrap: wrap;
}

.pos-tabs button, .pos-range button {
  border: 0;
  background: transparent;
  color: var(--albi-charcoal-70);
  padding: 8px 15px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  min-height: 38px;
  font-weight: 600;
  font-size: 14px;
  transition: background var(--dur-fast), color var(--dur-fast);
}

.pos-tabs button.on { background: var(--albi-charcoal); color: var(--albi-cream); }
.pos-range button.on { background: var(--albi-orange); color: var(--text-on-accent-strong); }
.pos-tabs button:not(.on):hover, .pos-range button:not(.on):hover { color: var(--albi-charcoal); }

.pos-range input[type="date"] {
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  min-height: 38px;
  font-size: 13px;
  color: var(--albi-charcoal);
}
.pos-range input[type="date"]:focus { outline: none; border-color: var(--albi-orange); }
.pos-range.dim { opacity: .4; pointer-events: none; }

/* ── KPI hero ───────────────────────────────────────────────────────────────── */

.kpi-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin-bottom: 30px;
}

.kpi {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-top: 3px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 16px 18px;
  min-width: 0;
  box-shadow: var(--shadow-xs);
}

.kpi.a-orange   { border-top-color: var(--albi-orange); }
.kpi.a-positive { border-top-color: var(--status-positive); }
.kpi.a-pink     { border-top-color: var(--albi-pink-deep); }
.kpi.a-danger   { border-top-color: var(--albi-orange-deep); }

.kpi b {
  display: block;
  font-family: var(--font-display);
  font-variation-settings: var(--display-bold);
  font-weight: 640;
  font-size: 27px;
  line-height: 1.05;
  color: var(--albi-charcoal);
  white-space: nowrap;
}
.kpi span { display: block; font-size: 13px; color: var(--albi-charcoal-70); margin-top: 5px; }
.kpi em { font-style: normal; font-size: 12px; color: var(--status-positive); font-weight: 700; }

/* ── section header ─────────────────────────────────────────────────────────── */

.pos-sec { display: flex; align-items: baseline; gap: 10px; margin: 6px 0 14px; flex-wrap: wrap; }
.pos-sec-hint { font-size: 12.5px; color: var(--albi-charcoal-70); }

.pos-note {
  background: var(--albi-cream-soft);
  border: 1px solid var(--albi-cream-deep);
  border-inline-start: 3px solid var(--albi-yellow-deep);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  font-size: 13px;
  color: var(--albi-charcoal-70);
  margin-bottom: 18px;
}

/* ── trend (revenue per Z) ──────────────────────────────────────────────────── */

.trend {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 8px 4px 0;
  margin-bottom: 34px;
  overflow-x: auto;
  min-height: 168px;
}

.trend-col { display: flex; flex-direction: column; align-items: center; gap: 6px; flex: 1 1 0; min-width: 46px; }
.trend-val { font-size: 11px; color: var(--albi-charcoal-70); white-space: nowrap; }

.trend-barwrap { width: 100%; height: 110px; display: flex; align-items: flex-end; justify-content: center; }

.trend-bar {
  width: 62%;
  max-width: 46px;
  min-height: 3px;
  border-radius: 8px 8px 3px 3px;
  background: linear-gradient(var(--albi-orange), var(--albi-orange-soft));
  transition: filter var(--dur-fast);
}
.trend-bar:hover { filter: brightness(1.05) saturate(1.1); }
.trend-lbl { font-size: 12px; color: var(--albi-charcoal-70); direction: ltr; unicode-bidi: isolate; }

/* ── two-column blocks (tender mix + top movers) ────────────────────────────── */

.pos-2col { display: grid; gap: 22px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

.pos-block {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-lg);
  padding: 18px 20px;
}
.pos-block .pos-sec { margin-top: 0; }

/* ── proportion bars ────────────────────────────────────────────────────────── */

.bars { display: grid; gap: 12px; }
.bar-row { display: grid; grid-template-columns: minmax(72px, 34%) 1fr auto; align-items: center; gap: 12px; }
.bar-label { font-size: 13.5px; color: var(--albi-charcoal); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 12px; background: var(--albi-cream-deep); border-radius: var(--radius-pill); overflow: hidden; }
.bar-fill { height: 100%; border-radius: var(--radius-pill); min-width: 4px; transition: width var(--dur-slow) var(--ease-out-soft); }
.bar-val { font-size: 13px; font-weight: 700; color: var(--albi-charcoal); white-space: nowrap; }

.bar-fill.k-credit  { background: var(--albi-orange); }
.bar-fill.k-cash    { background: var(--albi-yellow-deep); }
.bar-fill.k-hakafa  { background: var(--albi-pink-deep); }
.bar-fill.k-voucher { background: var(--albi-charcoal-40); }
.bar-fill.k-check   { background: var(--status-positive); }
.bar-fill.k-top     { background: linear-gradient(90deg, var(--albi-orange), var(--albi-orange-soft)); }
.bar-fill.k-default { background: var(--albi-charcoal-40); }

/* ── menu · products by category ────────────────────────────────────────────── */

.prod-cat { margin-bottom: 22px; }
.prod-cat-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.prod-cat-head h3 { font-size: 17px; }

.prod-cat-count {
  font-size: 12px; font-weight: 700; color: var(--albi-charcoal-70);
  background: var(--albi-cream-deep); border-radius: var(--radius-pill); padding: 1px 9px;
}

.prod-row {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 12px 16px;
  margin-bottom: 8px;
}
.prod-main { min-width: 0; }
.prod-name { display: block; font-weight: 600; color: var(--albi-charcoal); }
.prod-meta { display: flex; align-items: center; gap: 7px; margin-top: 5px; font-size: 12px; color: var(--albi-charcoal-70); }
.prod-meta .ltr { direction: ltr; unicode-bidi: isolate; }

.prod-nums { display: flex; gap: 18px; flex: none; text-align: center; }
.prod-price b, .prod-profit b { display: block; font-weight: 700; color: var(--albi-charcoal); font-size: 15px; }
.prod-price span, .prod-profit span { font-size: 11px; color: var(--albi-charcoal-70); }
.prod-profit b { color: var(--status-positive); }

.chip {
  display: inline-block; font-size: 11px; font-weight: 600;
  padding: 2px 9px; border-radius: var(--radius-pill);
  background: var(--albi-cream-soft); border: 1px solid var(--albi-cream-deep);
  color: var(--albi-charcoal-70);
}
.chip.muted { color: var(--albi-charcoal-70); }

/* ── team ───────────────────────────────────────────────────────────────────── */

.emp-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }

.emp {
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 15px 16px;
}
.emp.off { opacity: .6; }
.emp-top { display: flex; align-items: center; gap: 8px; }
.emp-name { font-weight: 700; color: var(--albi-charcoal); font-size: 15px; }
.emp-phone { font-size: 12.5px; color: var(--albi-charcoal-70); margin-top: 3px; direction: ltr; unicode-bidi: isolate; }
.emp-perms { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }

/* Worked hours on the roster card.
   Three states that must not look alike: a real figure, "did not work in this
   range", and "we could not read the clock". The last two are both muted but
   say different things — a zero that means "we do not know" is the bug this
   whole panel exists to stop repeating. */
.emp-hours { margin-top: 6px; font-size: 13px; color: var(--albi-charcoal); }
.emp-hours strong { font-weight: 700; }
.emp-hours.muted, .emp-last { font-size: 12px; color: var(--albi-charcoal-70); font-weight: 400; }
.emp-last { margin-top: 2px; direction: ltr; unicode-bidi: isolate; text-align: right; }

/* On the clock right now — the one card the operator is looking for. */
.emp.on-now { border-color: var(--albi-orange); box-shadow: 0 0 0 1px var(--albi-orange) inset; }

.chip.positive {
  background: color-mix(in srgb, var(--albi-orange) 16%, transparent);
  color: var(--albi-orange-deep);
  font-weight: 700;
}

/* ── states ─────────────────────────────────────────────────────────────────── */

.pos-loading { padding: 40px; text-align: center; color: var(--albi-charcoal-70); }
.pos-error { padding: 34px; text-align: center; display: grid; gap: 10px; justify-items: center; color: var(--albi-charcoal-70); }
.pos-error .sub { font-size: 12.5px; direction: ltr; unicode-bidi: isolate; }

td.neg { color: var(--albi-orange-deep); font-weight: 700; }

/* ── יומן · business calendar ─────────────────────────────────────────────── */

.cal-head { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-bottom: 16px; }
.cal-nav { display: flex; align-items: center; gap: 12px; }
.cal-nav h2 { min-width: 160px; text-align: center; }
.cal-nav button { min-width: 40px; font-size: 20px; line-height: 1; padding: 6px 12px; }
.cal-total { font-weight: 700; color: var(--albi-charcoal); background: var(--albi-cream-soft); border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-pill); padding: 8px 16px; }

.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
.cal-dow { text-align: center; font-size: 12.5px; font-weight: 700; color: var(--albi-charcoal-70); padding: 4px 0 8px; }
.cal-loading { grid-column: 1 / -1; padding: 40px; text-align: center; color: var(--albi-charcoal-70); }

.cal-cell {
  min-height: 104px; background: var(--albi-paper); border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md); padding: 7px 8px; cursor: pointer; overflow: hidden;
  display: flex; flex-direction: column; gap: 4px; transition: box-shadow var(--dur-fast), transform var(--dur-fast);
}
.cal-cell:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.cal-cell.empty { background: transparent; border: 0; cursor: default; }
.cal-cell.empty:hover { box-shadow: none; transform: none; }
.cal-cell.today { border-color: var(--albi-orange); box-shadow: inset 0 0 0 1px var(--albi-orange); }
.cal-cell.future { opacity: .82; }
.cal-cell-top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.cal-dnum { font-family: var(--font-display); font-variation-settings: var(--display-bold); font-weight: 640; font-size: 15px; color: var(--albi-charcoal); }
.cal-rev { font-size: 12px; font-weight: 700; color: var(--status-positive); white-space: nowrap; }

.cal-ev { font-size: 11.5px; background: var(--albi-charcoal); color: var(--albi-cream); border-radius: 7px; padding: 2px 7px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cal-shift { display: flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--albi-charcoal); overflow: hidden; }
.cal-shift-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; }
.cal-shift-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Dots standing in for the chips above. Hidden on desktop, shown when the cell
   is too narrow to hold a name — the month keeps its shape on a phone, it just
   stops pretending it can render text at 45px. */
.cal-marks { display: none; flex-wrap: wrap; gap: 3px; margin-top: auto; }
.cal-mark { width: 6px; height: 6px; border-radius: 50%; background: var(--albi-charcoal-40); }
.cal-mark.ev { background: var(--albi-charcoal); border-radius: 2px; width: 7px; }

/* mode switch — חודש / שבוע / יום */
.cal-modes { display: inline-flex; background: var(--albi-cream-soft); border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-pill); padding: 3px; gap: 2px; }

.cal-modes .seg {
  border: 0; background: transparent; color: var(--albi-charcoal-70); cursor: pointer;
  font: inherit; font-size: 13px; font-weight: 600; padding: 7px 15px; min-height: 38px;
  border-radius: var(--radius-pill); transition: background var(--dur-fast), color var(--dur-fast);
}
.cal-modes .seg.on { background: var(--albi-charcoal); color: var(--albi-cream); }

/* ── day / week time grid ─────────────────────────────────────────────────── */

.tg { border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-md); overflow: hidden; background: var(--albi-paper); }

/* ONE grid, two rows: the day headings and the hours share their columns, so
   they cannot drift apart when a scrollbar appears. See renderTimeGrid() for
   what the two-grid version got wrong. The headings are sticky, so scrolling to
   23:00 never loses which column is Tuesday. */
.tg-scroll {
  position: relative;
  display: grid;
  grid-template-columns: 54px minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  max-height: 62vh;
  overflow-y: auto;
  overscroll-behavior: contain;

  /* Reserve the scrollbar's width whether or not it is showing, so a week that
     happens to fit does not sit 15px wider than one that does not — and style
     it, because the platform default is a grey slab in a cream console. */
  scrollbar-gutter: stable;
  scrollbar-width: thin;
  scrollbar-color: var(--albi-cream-deep) transparent;
}
.tg-scroll::-webkit-scrollbar { width: 10px; }
.tg-scroll::-webkit-scrollbar-track { background: transparent; }

.tg-scroll::-webkit-scrollbar-thumb {
  background: var(--albi-cream-deep);
  border-radius: 999px;
  border: 3px solid var(--albi-paper);   /* inset, so the thumb reads as a pill */
}
.tg-scroll::-webkit-scrollbar-thumb:hover { background: var(--albi-charcoal-40); }

.tg-corner {
  grid-area: 1 / 1; position: sticky; top: 0; z-index: 4;
  background: var(--albi-cream-soft);
  border-inline-end: 1px solid var(--albi-cream-deep);
  border-bottom: 1px solid var(--albi-cream-deep);
}

.tg-head {
  grid-area: 1 / 2; position: sticky; top: 0; z-index: 3;
  display: grid; grid-template-columns: repeat(var(--tg-cols, 7), minmax(0, 1fr));
  background: var(--albi-cream-soft);
  border-bottom: 1px solid var(--albi-cream-deep);
}
.tg-colhead { display: flex; flex-direction: column; align-items: center; gap: 1px; padding: 8px 4px; cursor: pointer; border-inline-start: 1px solid var(--albi-cream-deep); min-width: 0; }
.tg-colhead:first-child { border-inline-start: 0; }
.tg-colhead.today { background: var(--albi-orange); color: var(--albi-cream); }
.tg-colhead.today .tg-rev { color: var(--albi-cream); }
.tg-dow { font-size: 11.5px; font-weight: 700; opacity: .8; }
.tg-dnum { font-family: var(--font-display); font-variation-settings: var(--display-bold); font-weight: 640; font-size: 17px; }
.tg-rev { font-size: 11px; font-weight: 700; color: var(--status-positive); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }

.tg-gutter-col { grid-area: 2 / 1; position: relative; border-inline-end: 1px solid var(--albi-cream-deep); }
.tg-hour { position: absolute; inset-inline-end: 6px; transform: translateY(-50%); font-size: 11px; color: var(--albi-charcoal-40); direction: ltr; unicode-bidi: isolate; white-space: nowrap; }

/* `touch-action: none` is what makes a drag on a phone a drag and not a scroll.
   Without it the browser claims the gesture for panning before pointermove
   ever fires, and the block never moves. Scoped to the grid, so the page and
   the hour column still scroll normally. See shift-grid.js. */
.tg-grid { grid-area: 2 / 2; position: relative; display: grid; grid-template-columns: repeat(var(--tg-cols, 7), minmax(0, 1fr)); touch-action: none; }
.tg-rule { position: absolute; inset-inline: 0; height: 1px; background: var(--albi-cream-deep); pointer-events: none; }
.tg-col { position: relative; grid-row: 1; border-inline-start: 1px solid var(--albi-cream-deep); cursor: cell; min-width: 0; }
.tg-col:first-child { border-inline-start: 0; }
.tg-col.today { background: color-mix(in srgb, var(--albi-orange) 6%, transparent); }

/* `inset-inline-start` and `width` are set per block by packLanes() in
   calendar.js, so two people on the same evening sit side by side instead of
   one drawing over the other. A single entry is still full width. */
.tg-block {
  position: absolute; border-radius: var(--radius-sm); padding: 3px 6px;
  color: var(--albi-white); overflow: hidden; display: flex; flex-direction: column; gap: 1px;
  font-size: 11.5px; line-height: 1.25; cursor: pointer; box-shadow: var(--shadow-sm);
}
.tg-block.ev { background: var(--albi-charcoal); outline: 2px solid var(--albi-orange); outline-offset: -2px; }
.tg-block-n { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tg-block-t { font-size: 10.5px; opacity: .85; direction: ltr; unicode-bidi: isolate; }
.tg-now { position: absolute; inset-inline: 0; height: 2px; background: var(--albi-orange-deep); z-index: 2; pointer-events: none; }
.tg-now::before { content: ''; position: absolute; inset-inline-start: -3px; top: -3px; width: 8px; height: 8px; border-radius: 50%; background: var(--albi-orange-deep); }

/* ── worked hours, as recorded by the POS clock ───────────────────────────── */

/* Hollow, not filled. A solid block on this grid is a PLAN somebody made; an
   outlined one is a FACT the clock recorded. Two different objects that used to
   be impossible to tell apart because only one of them was ever drawn. */
.tg-block.worked {
  background: color-mix(in srgb, var(--w-color, var(--albi-charcoal-40)) 14%, var(--albi-paper)) !important;
  border: 1.5px solid var(--w-color, var(--albi-charcoal-40));
  color: var(--albi-charcoal);
  box-shadow: none;
  cursor: default;
}

.tg-block.worked .tg-block-t { opacity: 1; color: var(--albi-charcoal-70); }

/* Still clocked in: the block runs to "now" and says so. */
.tg-block.worked.open { border-style: dashed; }

.tg-block.worked.open::after {
  content: ''; position: absolute; inset-inline-start: 5px; top: 5px;
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--status-positive);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-positive) 25%, transparent);
}

.cal-shift.worked .cal-shift-nm { color: var(--albi-charcoal-70); }
.cal-shift-dot.hollow { background: transparent !important; border: 2px solid var(--albi-charcoal-40); box-sizing: border-box; }

/* ── "who is on shift right now" ──────────────────────────────────────────── */

.cal-now {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 12px; padding: 8px 12px;
  background: color-mix(in srgb, var(--status-positive) 10%, var(--albi-paper));
  border: 1px solid color-mix(in srgb, var(--status-positive) 30%, transparent);
  border-radius: var(--radius-md);
}

.cal-now-label { font-size: 12px; font-weight: 700; color: var(--albi-charcoal-70); }
.cal-now-chip { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; background: var(--albi-white); border-radius: 999px; font-size: 13px; }
.cal-now-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.cal-now-since, .cal-now-h { font-size: 11.5px; color: var(--albi-charcoal-40); direction: ltr; unicode-bidi: isolate; }

/* ── direct manipulation (shift-grid.js) ──────────────────────────────────── */

.tg-block.editable { cursor: grab; touch-action: none; }
.tg-block.dragging { cursor: grabbing; opacity: .9; z-index: 5; box-shadow: var(--shadow-md); }

/* The resize grip. A bar rather than a corner: the whole bottom edge is the
   target, because the thing being adjusted is when the shift ENDS. */
.tg-handle {
  position: absolute; inset-inline: 0; bottom: 0; height: 10px;
  cursor: ns-resize;
}

.tg-handle::after {
  content: ''; position: absolute; inset-inline: 35%; bottom: 3px; height: 2px;
  border-radius: 2px; background: color-mix(in srgb, var(--albi-white) 70%, transparent);
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-out-soft);
}

.tg-block:hover .tg-handle::after { opacity: 1; }

/* The selection drawn while dragging out a new shift — FullCalendar's
   `selectHelper`, in eight lines. */
.tg-ghost {
  position: absolute; inset-inline: 3px; z-index: 4; pointer-events: none;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--albi-orange) 28%, transparent);
  border: 1.5px dashed var(--albi-orange-deep);
  color: var(--albi-charcoal); font-size: 11px; font-weight: 700;
  padding: 2px 5px; direction: ltr; unicode-bidi: isolate;
}

/* The editor. A bubble over the block, not a modal over the page: building a
   rota is twenty small edits in a row, and blanking the week to make each one
   hides the very thing being decided against. */
.tg-bubble {
  position: absolute; z-index: 60; width: 260px;
  display: grid; gap: 8px;
  padding: 12px;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}

.tg-bubble-when { font-size: 12.5px; font-weight: 700; color: var(--albi-charcoal-70); }
.tg-bubble-times { display: flex; align-items: center; gap: 6px; }
.tg-bubble-times input { flex: 1; min-width: 0; }
.tg-bubble select, .tg-bubble input { width: 100%; padding: 7px 9px; border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-sm); background: var(--albi-white); font: inherit; font-size: 13.5px; }
.tg-bubble-row { display: flex; align-items: center; gap: 8px; }
.tg-bubble-del { margin-inline-start: auto; border: 0; background: transparent; color: var(--status-danger); font: inherit; font-size: 12.5px; cursor: pointer; padding: 4px; }
.tg-bubble-del:hover { text-decoration: underline; }

/* day dialog */
.day-money { font-weight: 700; color: var(--status-positive); margin-bottom: 6px; }

/* `dialog h4 { margin-top: 4px }` used to sit here, 430 lines after the rule
   that sets every other property on the same selector. It changed one value
   for every dialog in the console from inside a block labelled "day dialog",
   which is where you would look for it last. Folded into the rule above. */
.day-shifts { display: grid; gap: 7px; margin-bottom: 6px; }
.day-shift { display: flex; align-items: center; gap: 9px; background: var(--albi-cream-soft); border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-sm); padding: 8px 11px; }
.day-shift-nm { font-weight: 600; color: var(--albi-charcoal); }
.day-shift-hr { font-size: 12.5px; color: var(--albi-charcoal-70); direction: ltr; unicode-bidi: isolate; }
.day-del { margin-inline-start: auto; border: 0; background: transparent; color: var(--albi-charcoal-40); font-size: 20px; line-height: 1; cursor: pointer; padding: 0 4px; }
.day-del:hover { color: var(--albi-orange-deep); }
.day-line { padding: 6px 0; color: var(--albi-charcoal); }
.day-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.day-add select, .day-add input { flex: 1 1 110px; min-width: 90px; padding: 9px 11px; border: 1.5px solid var(--albi-charcoal-40); border-radius: var(--radius-sm); background: var(--albi-white); min-height: 42px; }
.day-add input[type="time"] { flex: 0 0 auto; }
.day-add .primary { flex: 0 0 auto; }

/* ── צוות · staff roster ──────────────────────────────────────────────────── */

.staff-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); }
.staff-card { display: flex; align-items: center; gap: 13px; background: var(--albi-paper); border: 1px solid var(--albi-cream-deep); border-radius: var(--radius-md); padding: 13px 15px; cursor: pointer; transition: box-shadow var(--dur-fast), transform var(--dur-fast); }
.staff-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); }
.staff-card.off { opacity: .55; }
.staff-av { flex: none; width: 46px; height: 46px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--albi-white); font-weight: 700; font-size: 16px; overflow: hidden; }
.staff-av img { width: 100%; height: 100%; object-fit: cover; }
.staff-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.staff-nm { font-weight: 700; color: var(--albi-charcoal); }
.staff-role { font-size: 12.5px; color: var(--albi-charcoal-70); }
.staff-ph { font-size: 12px; color: var(--albi-charcoal-70); direction: ltr; unicode-bidi: isolate; }

@media (prefers-reduced-motion: reduce) {
  .card { transition: none; }
  .bar-fill, .trend-bar { transition: none; }
  .cal-cell, .staff-card { transition: none; }
}

/* ── phones ───────────────────────────────────────────────────────────────────
   Until now this file had no width query at all, so the console rendered its
   desktop layout on a 390px screen: a 7-column month grid gives ~45px cells,
   which cannot hold "יוסי · 17:00", and the top nav ran off the side.
   The manager reads this standing in the venue, on a phone. Treat that as the
   primary case and let the desktop keep what it had. */
@media (width <= 720px) {
  /* Nav scrolls sideways instead of wrapping into three ragged rows. Momentum
     scrolling with no visible bar, so it reads as a strip, not a scroller. */
  header nav {
    display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch; padding-bottom: 2px;
  }
  header nav::-webkit-scrollbar { display: none; }
  header nav button { flex: none; white-space: nowrap; }

  .cal-head { gap: 10px; }
  .cal-modes { width: 100%; justify-content: stretch; }
  .cal-modes .seg { flex: 1; padding: 7px 6px; }
  .cal-nav { width: 100%; justify-content: space-between; }
  .cal-nav h2 { min-width: 0; font-size: 17px; }
  .cal-total { width: 100%; text-align: center; }

  /* Month: dots, not text. The chips would be ellipsised to nothing anyway. */
  .cal-grid { gap: 3px; }
  .cal-cell { min-height: 62px; padding: 4px; border-radius: var(--radius-sm); }
  .cal-cell .cal-ev, .cal-cell .cal-shift { display: none; }
  .cal-marks { display: flex; }
  .cal-dnum { font-size: 13px; }
  .cal-rev { display: none; }
  .cal-dow { font-size: 11px; padding: 2px 0 5px; }

  /* Time grid: a narrower gutter and taller touch targets. The week view keeps
     seven columns and simply scrolls — collapsing it to a list would lose the
     side-by-side comparison that is the only reason to look at a week. */
  .tg-scroll { max-height: 68vh; grid-template-columns: 40px minmax(0, 1fr); }
  .tg-hour { font-size: 10px; inset-inline-end: 4px; }
  .tg-dnum { font-size: 15px; }
  .tg-rev { display: none; }
  .tg-block { font-size: 10.5px; padding: 2px 4px; inset-inline: 2px; }
  .tg-day .tg-block { font-size: 12px; padding: 4px 8px; }

  /* Dialogs: full-bleed sheets. A centred 660px box on a phone is a letterbox
     with the action row off the bottom. */
  dialog, dialog.wide {
    max-width: 100%; width: 100%; max-height: 92dvh;
    margin: auto auto 0; border-radius: var(--radius-md) var(--radius-md) 0 0;
  }
  .day-add { gap: 6px; }
  .day-add select { flex: 1 1 100%; }
  .day-add input[type="time"] { flex: 1 1 calc(50% - 3px); }

  .staff-grid { grid-template-columns: 1fr; }
  .grid2 { grid-template-columns: 1fr; }
}

/* ── תפעול · the operations pages ─────────────────────────────────────────────
   One tab per recurring manual loop (ops.js maps each to the mailbox audit).
   Everything here is a list of documents or a table of months, so the vocabulary
   stays deliberately small: a row, a pill, a note strip. No new colours — the
   status pills reuse the tokens the event pills already use, so "orange means
   this still needs you" keeps meaning that across the whole console. */

.ops-head {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

/* The count on a tab. Inline rather than absolutely positioned, so a two-digit
   badge widens its tab instead of overlapping the next one. */
.pos-tabs button .badge {
  font-style: normal;
  margin-inline-start: 6px;
  background: var(--albi-orange);
  color: var(--text-on-accent-strong);
  border-radius: var(--radius-pill);
  padding: 1px 7px;
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}
.pos-tabs button.on .badge { background: var(--albi-cream); color: var(--albi-charcoal); }

/* The "why this page exists" strip. These pages replace a habit, and a habit is
   only replaced by something that says what it is for. */
.ops-note {
  display: flex;
  flex-direction: column;
  gap: 3px;
  background: var(--albi-cream-soft);
  border-inline-start: 3px solid var(--albi-charcoal-40);
  border-radius: var(--radius-md);
  padding: 11px 14px;
  margin-bottom: 16px;
}
.ops-note strong { font-size: 14.5px; }
.ops-note span { font-size: 13px; color: var(--albi-charcoal-70); }
.ops-note.warn { border-inline-start-color: var(--albi-orange); }

.ops-sub { margin: 22px 0 10px; font-size: 14px; color: var(--albi-charcoal-70); font-weight: 600; }

.ops-list { display: flex; flex-direction: column; gap: 6px; }

.ops-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  width: 100%;
  text-align: start;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 11px 14px;
  cursor: pointer;
  font: inherit;
  color: inherit;
}
.ops-row:hover { border-color: var(--albi-orange); }
.ops-row.static { cursor: default; }
.ops-row.static:hover { border-color: var(--albi-cream-deep); }

.ops-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ops-row-main strong { font-size: 15px; }
.ops-row-main .cp { font-size: 13px; color: var(--albi-charcoal-70); }

.ops-row-meta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ops-row-meta time { font-size: 12.5px; color: var(--albi-charcoal-70); }
.ops-row-meta .amt { font-variant-numeric: tabular-nums; font-weight: 600; }

/* Status. `new` is the only one that shouts, because it is the only one that
   means a person still has to do something today. */
.pill.st-new         { background: var(--albi-orange); color: var(--text-on-accent-strong); }
.pill.st-checked     { background: var(--status-positive); color: var(--albi-cream); }
.pill.st-discrepancy { background: var(--albi-orange-deep); color: var(--albi-cream); }
.pill.st-awaiting    { background: var(--albi-charcoal-40); color: var(--albi-charcoal); }
.pill.st-resolved    { background: var(--status-positive); color: var(--albi-cream); }
.pill.st-filed       { background: var(--albi-charcoal); color: var(--albi-cream); }
.pill.warn           { background: var(--albi-orange-deep); color: var(--albi-cream); }
.pill.ghost          { background: transparent; border: 1px solid var(--albi-charcoal-40); color: var(--albi-charcoal-70); }

.ops-empty {
  text-align: center;
  padding: 34px 18px;
  color: var(--albi-charcoal-70);
  background: var(--albi-cream-soft);
  border-radius: var(--radius-md);
}
.ops-empty h4 { margin: 0 0 6px; font-size: 15px; color: var(--albi-charcoal); }
.ops-empty p { font-size: 13.5px; margin: 0 0 8px; }

.ops-monthbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--albi-cream-soft);
  border-radius: var(--radius-pill);
  padding: 8px 14px;
  margin-bottom: 16px;
}

.ops-monthbar input[type="month"] {
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-pill);
  padding: 6px 12px;
  min-height: 38px;
  font: inherit;
}
.ops-monthbar .big { font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums; }

/* ── one document: the file beside the work ──────────────────────────────── */

/* dialog.wide expects a <form> child for its scrolling; these dialogs are built
   from plain elements, so they declare their own column layout rather than
   inheriting a rule written for the event form. */
dialog.ops-doc[open] { flex-direction: column; gap: 12px; }
.ops-doc-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ops-doc-head h3 { margin: 0; }
.ops-doc-head .cp { color: var(--albi-charcoal-70); font-size: 14px; }

.ops-doc-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: 14px;
  min-height: 0;
  overflow: hidden;
}
.ops-doc-file { min-height: 320px; display: flex; }

.ops-doc-file iframe {
  width: 100%;
  min-height: 320px;
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  background: var(--albi-white);
}
.ops-doc-side { overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 10px; }

.ops-events { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.ops-events li { display: flex; gap: 8px; font-size: 12.5px; color: var(--albi-charcoal-70); }
.ops-events .when { font-variant-numeric: tabular-nums; min-width: 52px; }
.ops-events .who { margin-inline-start: auto; }

.ops-fields label { display: block; margin-bottom: 10px; font-size: 13.5px; font-weight: 600; }

.ops-fields input, .ops-fields select {
  width: 100%;
  margin-top: 4px;
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-md);
  padding: 8px 10px;
  font: inherit;
}

/* ── the receiving checklist ─────────────────────────────────────────────── */
.ops-check-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.ops-check-row { display: flex; gap: 5px; align-items: center; }

.ops-check-row input, .ops-check-row select {
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-md);
  padding: 7px 8px;
  font: inherit;
  min-width: 0;
  width: 72px;
}
.ops-check-row input.grow { flex: 1 1 auto; width: auto; }
.ops-check-row select { width: auto; flex: 0 0 auto; }

/* ── payroll ─────────────────────────────────────────────────────────────── */
.ops-pay { display: flex; flex-direction: column; gap: 4px; margin-bottom: 16px; }

.ops-pay-row {
  display: grid;
  grid-template-columns: 1.4fr 1fr 82px 82px 60px 60px;
  gap: 8px;
  align-items: center;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 8px 12px;
}

.ops-pay-row.head {
  background: transparent;
  border: 0;
  font-size: 12px;
  color: var(--albi-charcoal-70);
  font-weight: 600;
  padding-bottom: 0;
}
.ops-pay-row .name { font-weight: 600; }

/* What the schedule says, beside what will be sent. Muted, because it is
   evidence rather than an input — but never hidden. */
.ops-pay-row .calc { font-size: 13px; color: var(--albi-charcoal-70); font-variant-numeric: tabular-nums; }

.ops-pay-row input[type="number"] {
  width: 100%;
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-md);
  padding: 6px 8px;
  font: inherit;
  font-variant-numeric: tabular-nums;
}
.ops-pay-row .chk { display: flex; justify-content: center; }
.ops-pay-row .chk input { width: 20px; height: 20px; accent-color: var(--albi-orange); }

@media (width <= 860px) {
  .ops-doc-grid { grid-template-columns: 1fr; overflow-y: auto; }
  .ops-doc-file { min-height: 240px; }
  .ops-pay-row { grid-template-columns: 1fr 1fr; row-gap: 6px; }
  .ops-pay-row.head { display: none; }
  .ops-check-row { flex-wrap: wrap; }
  .ops-check-row input.grow { flex: 1 1 100%; }
}

/* ── movement · see ui.js ────────────────────────────────────────────────────
   Three pieces: the network bar, the skeletons that hold a section's place
   while its data is in flight, and the cross-fade between views. All three are
   decoration and every one of them is disabled under prefers-reduced-motion —
   the console still works identically with the whole block switched off. */

/* The one honest "something is loading" signal in the app, ref-counted across
   every view. Fixed to the top of the viewport, above everything, and never
   interactive. */
.net-bar {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  z-index: 999;
  background: transparent;
  pointer-events: none;
  overflow: hidden;
}

.net-bar.on::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inline-size: 40%;
  background: linear-gradient(90deg, transparent, var(--albi-orange), transparent);
  animation: net-slide 1.1s ease-in-out infinite;
}

/* Travels along the inline axis. In this RTL document that reads right-to-left,
   which is the direction the eye already scans. */
@keyframes net-slide {
  from { inset-inline-start: -40%; }
  to   { inset-inline-start: 100%; }
}

/* A section's shape, before its content exists. Never shows a number — see the
   note in ui.js about why stale values are worse than absent ones. */
.skel { display: flex; flex-direction: column; gap: 6px; }

.skel-row {
  block-size: 44px;
  inline-size: var(--w, 100%);
  border-radius: var(--radius-md);
  background: linear-gradient(
    90deg,
    var(--albi-cream-soft) 0%,
    var(--albi-cream-deep) 50%,
    var(--albi-cream-soft) 100%
  );
  background-size: 200% 100%;
  animation: skel-sheen 1.4s ease-in-out infinite;
}
.skel-cards .skel-row { block-size: 116px; }
.skel-pay .skel-row { block-size: 38px; }

@keyframes skel-sheen {
  from { background-position: 100% 0; }
  to   { background-position: -100% 0; }
}

/* ── the paper index ─────────────────────────────────────────────────────────
   One card per class of paper. The count is the subject of the card, so it is
   the biggest thing on it; the orange dot is the only element that says
   "something here still needs you", which keeps the grid scannable at a glance
   rather than requiring it to be read. */
.paper-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
}

.paper-card {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: start;
  background: var(--albi-paper);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-md);
  padding: 14px 16px 16px;
  cursor: pointer;
  font: inherit;
  color: inherit;
  transition: transform .14s, border-color .14s;
}
.paper-card:hover { transform: translateY(-2px); border-color: var(--albi-orange); }

@media (prefers-reduced-motion: reduce) {
  .paper-card { transition: none; }
  .paper-card:hover { transform: none; }
}

.paper-top { display: flex; align-items: center; gap: 8px; }
.paper-top strong { font-size: 15px; }

.paper-count {
  font-family: var(--font-display);
  font-variation-settings: var(--display-bold);
  font-size: 30px;
  line-height: 1.15;
  color: var(--albi-charcoal);
  font-variant-numeric: tabular-nums;
}
.paper-sub { font-size: 12.5px; color: var(--albi-charcoal-70); }

/* How many of this class nobody has looked at yet. */
.badge-dot {
  background: var(--albi-orange);
  color: var(--text-on-accent-strong);
  border-radius: var(--radius-pill);
  padding: 1px 8px;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* The par-level box on an item row. Narrow and quiet: it is an occasional
   adjustment, not the row's subject. */
.ops-row-meta .par {
  inline-size: 88px;
  border: 1.5px solid var(--albi-charcoal-40);
  background: var(--albi-white);
  border-radius: var(--radius-md);
  padding: 5px 8px;
  font: inherit;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* A value that has not arrived yet, in a bar that is already on screen. */
.ops-monthbar .muted { color: var(--albi-charcoal-40); }
.ops-slotline { display: inline-flex; align-items: center; gap: 8px; min-block-size: 34px; }

/* The view cross-fade. ::view-transition-* only exist where the API does, so
   this whole block is inert in browsers without it. */
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root) { animation: vt-out 130ms ease both; }
  ::view-transition-new(root) { animation: vt-in 190ms ease both; }
}

@keyframes vt-out { to { opacity: 0; } }

@keyframes vt-in { from { opacity: 0; transform: translateY(4px); } }

@media (prefers-reduced-motion: reduce) {
  .net-bar.on::before,
  .skel-row { animation: none; }

  /* Still legible without the sheen — the block itself is the affordance. */
  .skel-row { background: var(--albi-cream-soft); }
}

/* ── the documents gallery ──────────────────────────────────────────────────
   Cards of paper, not rows of text. See admin/public/docthumbs.js for why the
   thumbnails are rendered in the browser and cached in R2 rather than made on
   the server or made on every view. */

.doc-grid {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}

.doc-card {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: 0;
  overflow: hidden;
  text-align: start;
  font: inherit;
  color: inherit;
  cursor: pointer;
  background: var(--albi-cream-soft);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-lg, 22px);
  box-shadow: var(--shadow-xs);
  transition: transform var(--dur-mid, 180ms) var(--ease-out-soft, ease),
              box-shadow var(--dur-mid, 180ms) var(--ease-out-soft, ease);
}
.doc-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg, 0 16px 40px rgb(76 73 69 / 16%)); }
.doc-card:focus-visible { outline: 2px solid var(--albi-orange); outline-offset: 2px; }

/* THE ASPECT RATIO IS THE POINT. The box is A4 (1 : √2) and is reserved before
   any image exists, so a thumbnail landing from the warm-up queue never shifts
   the grid under the cursor — the single thing that would make a gallery that
   fills in progressively feel worse than the table it replaced. */
.doc-card-shot {
  position: relative;
  aspect-ratio: 1 / 1.414;
  background: var(--albi-white);
  border-block-end: 1px solid var(--albi-cream-deep);
  display: grid;
  place-items: center;
  overflow: hidden;
}

.doc-card-shot img {
  inline-size: 100%;
  block-size: 100%;

  /* `cover` with top alignment: a document's identity — letterhead, supplier
     name, document number — is in its top third. Fitting the whole page inside
     the card would letterbox it and shrink exactly that part to nothing. */
  object-fit: cover;
  object-position: top center;
  display: block;
}

/* Three states that are not "an image". Distinguished on purpose: a document
   with no file at all is a different problem from one whose thumbnail has not
   been drawn yet, and showing the same grey square for both would hide it. */
.doc-card-shot::after {
  position: absolute;
  inset-block-end: 8px;
  inset-inline-start: 8px;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--albi-cream-deep);
  color: var(--albi-charcoal-70);
}
.doc-card-shot.is-working::after { content: 'מכינה תצוגה…'; }
.doc-card-shot.is-pending::after { content: 'אין תצוגה'; }
.doc-card-shot.is-missing { background: var(--albi-cream); }
.doc-card-nofile { font-size: 12px; font-weight: 700; color: var(--albi-charcoal-40); }

/* The sheen while a page is being drawn — the same idiom as .skel-row, so a
   warming card reads as "working" rather than "broken". */
.doc-card-shot.is-working {
  background: linear-gradient(100deg,
    var(--albi-cream-soft) 30%, var(--albi-white) 50%, var(--albi-cream-soft) 70%);
  background-size: 220% 100%;
  animation: doc-shimmer 1.4s ease-in-out infinite;
}

@keyframes doc-shimmer { from { background-position: 180% 0; } to { background-position: -40% 0; } }

.doc-card-body { display: flex; flex-direction: column; gap: 4px; padding: 10px 12px 12px; }
.doc-card-body strong { font-size: 14px; line-height: 1.25; }
.doc-card-body .cp { font-size: 12px; color: var(--albi-charcoal-70); }
.doc-card-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-block-start: 2px; }
.doc-card-meta time { font-size: 11px; color: var(--albi-charcoal-40); }
.doc-card-meta .amt { font-size: 12px; font-weight: 700; margin-inline-start: auto; }

/* The gallery/table switch. */

.doc-modes {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--albi-cream-soft);
  border: 1px solid var(--albi-cream-deep);
  border-radius: var(--radius-pill);
}

.doc-modes button {
  border: 0;
  background: none;
  font: inherit;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--albi-charcoal-70);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  cursor: pointer;
}

.doc-modes button.on { background: var(--albi-white); color: var(--albi-charcoal); box-shadow: var(--shadow-xs); }

@media (prefers-reduced-motion: reduce) {
  .doc-card:hover { transform: none; }
  .doc-card-shot.is-working { animation: none; background: var(--albi-cream-soft); }
}
