/* ==========================================================================
   Opeerion ONE — design language per opeerion.com
   Ink #05070c · blue #0258d3 · magenta #e13dea · Geist
   ========================================================================== */

/* Geist is a variable font: one file covers weights 100-900. */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/geist-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/geist-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/geist-mono-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist Mono';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url(fonts/geist-mono-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* The app is dark throughout. Telling the browser so makes the *native*
     controls follow: the date/time picker panels, their calendar icons, form
     dropdowns and scrollbars. Without it Chrome renders the date picker as a
     white panel and its indicator icon dark-on-dark, all but invisible. */
  color-scheme: dark;

  /* Brand ramp — the three official stops, 135° diagonal. */
  --blue: #0258d3;
  --mid: #7b2fd6;
  --magenta: #e13dea;
  --accent-gradient: linear-gradient(135deg, #0258d3, #7b2fd6, #e13dea);
  /* The ring drawn round a focused field. Lifted a shade off the brand purple
     so it clears 3:1 against the card behind it (WCAG 1.4.11); #7b2fd6 measured
     2.98:1 there. The difference is invisible in use and the ring is only drawn
     on focus, so nothing changes for anyone not tabbing through a form. */
  --focus-ring: #8640d9;
  --focus-halo: #8640d973;

  /* The edge of something you can operate -- a field, a select, a plain button.
     Held apart from --border on purpose: WCAG 1.4.11 asks 3:1 of a control's
     boundary and asks nothing of a decorative hairline, so cards, table rules
     and dividers keep the lighter --border and the page keeps its weight.
     #ffffff29 measured 1.50:1; this is 3.09:1 against both the field fill and
     the card behind it. */
  --border-control: #ffffff57;
  --border-control-hi: #ffffff80;
  /* A field on this theme sits on a card only a shade lighter than itself, so
     the edge was doing all the work of saying "this is a box". A dark inner lip
     gives it depth as well, the same trick the cards already use in reverse. */
  --control-inset: inset 0 1px 2px #00000073;
  --accent-bar: linear-gradient(90deg, #0258d3, #e13dea);

  /* Surfaces: ink, ink-soft, and one step above for controls. */
  --bg: #05070c;
  --panel: #0a0d15;
  --panel-2: #121826;

  /* Hairlines, per the brand spec, on dark grounds. */
  --border: #ffffff29;
  --border-strong: #ffffff40;

  --text: #f2f4f8;
  /* Brand muted (#636873) only clears 3.7:1 on ink, so body-muted text is
     lifted to stay readable; the brand value stays for lines and dividers. */
  --muted: #8a92a3;
  --muted-deep: #636873;

  /* Links need more lift than brand blue gives on an ink ground. */
  --accent: #6aa5ff;
  --accent-2: #0258d3;

  --green: #35c47b;
  --amber: #e0a93f;
  --red: #ff6b6b;
  /* Amber, deliberately not red: red already means cancelled in the legend. */
  --holiday: #e8b45a;
  --purple: #b07ff0;
  --radius: 12px;

  --mono: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Status families, as background / text / border. They were written inline
     on about forty badge classes; naming them is what lets a light theme
     restate them rather than wash them out. */
  --ok-bg: #123524;   --ok-fg: #5fd68f;   --ok-bd: #1e5c3c;
  --info-bg: #14263f; --info-fg: #6fb0ff; --info-bd: #1e3f66;
  --warn-bg: #33270f; --warn-fg: #e8b45a; --warn-bd: #5c4a1e;
  --bad-bg: #331414;  --bad-fg: #f08a8a;  --bad-bd: #5c2424;
  --bad-bg2: #4a1414; --bad-fg2: #ff9a9a; --bad-bd2: #6b2020;
  --note-bg: #201436; --note-fg: #b795f5; --note-bd: #3c2766;
  --neut-bg: #2a2f38; --neut-fg: #aab6c5; --neut-bd: #3d4654;
  --off-bg: #2a2a2a;  --off-fg: #999;     --off-bd: #444;

  /* Washes laid over the ground: light on dark here, dark on light there. */
  --tint-1: #ffffff08;
  --tint-2: #ffffff0a;
  --tint-3: #ffffff0d;
  --tint-4: #ffffff0f;
  --tint-5: #ffffff12;
  --tint-6: #ffffff14;
  --tint-7: #ffffff33;
  --grid: #ffffff0a;
  --shadow: 0 1px 2px #00000040;

  /* Lines, not lettering. A coloured bar has to hold its own at three pixels,
     which is the opposite of what a coloured word needs on a pale ground —
     so these never double as text colours. */
  --ok-line: #5fd68f;
  --warn-line: #e8b45a;
  --info-line: #6fb0ff;
  --note-line: #b795f5;
  --bad-line: #f08a8a;
  /* Was #4a8fd8, which was four degrees of hue from the default card edge and
     two from Blue -- so "Steel" was not a colour anybody could see they had
     chosen. Teal is 42 degrees off the default and 29 off Green. */
  --cool-line: #2fbfae;
  --lime-line: #b6d94a;
  --pink-line: #f07ad0;
  --dim-line: #555;
}

/* ==========================================================================
   Light mode
   Not the dark theme lightened. A dark UI hides its edges in shadow; a light
   one has to draw them, so the hairlines are darker in proportion, the
   controls sit a step below the card they are on rather than a step above,
   and every status colour is restated for contrast on white instead of being
   bleached. Set by the user from the header; the server writes the choice
   into the html element so the first paint is already right.
   ========================================================================== */
:root[data-theme="light"] {
  color-scheme: light;

  /* Page is the tint, cards are the white. That ordering is what stops a
     light UI reading as one flat sheet. */
  --bg: #eef1f6;
  --panel: #ffffff;
  --panel-2: #e7ebf2;

  /* Hairlines have to carry the structure that shadow carries on dark. */
  --border: #0f172a24;
  --border-strong: #0f172a45;

  --text: #0d1220;
  --muted: #566071;
  --muted-deep: #6b7480;

  /* Brand blue is only 4.1:1 on white, so links take the darker stop and the
     brand value stays for fills, where it sits under white text. */
  --accent: #0247ab;
  --accent-2: #0258d3;
  --focus-ring: #7b2fd6;
  --focus-halo: #7b2fd659;
  --border-control: #0f172a78;
  --border-control-hi: #0f172aa0;
  --control-inset: inset 0 1px 2px #0f172a14;

  --green: #17794a;
  --amber: #8a5a00;
  --red: #c02626;
  --holiday: #8a5a00;
  --purple: #6b35c9;

  --ok-bg: #e3f5ea;   --ok-fg: #12633c;   --ok-bd: #a5d9bd;
  --info-bg: #e6effc; --info-fg: #0b4a94; --info-bd: #a6c6ec;
  --warn-bg: #fcf2df; --warn-fg: #7a5410; --warn-bd: #e3c689;
  --bad-bg: #fdeaea;  --bad-fg: #9b2226;  --bad-bd: #eeb0b0;
  --bad-bg2: #fbdcdc; --bad-fg2: #8a1b1f; --bad-bd2: #e59a9a;
  --note-bg: #f1eafd; --note-fg: #5b2fa8; --note-bd: #cab2ef;
  --neut-bg: #e9edf3; --neut-fg: #465060; --neut-bd: #c9d2de;
  --off-bg: #ececef;  --off-fg: #656d7a;  --off-bd: #cfd4db;

  --tint-1: #0f172a08;
  --tint-2: #0f172a0d;
  --tint-3: #0f172a12;
  --tint-4: #0f172a17;
  --tint-5: #0f172a1c;
  --tint-6: #0f172a24;
  --tint-7: #0f172a3d;
  --grid: #0f172a0a;
  --shadow: 0 1px 2px #0f172a1f;

  /* Same hues as the dark set, and deliberately the same weight. Darkening
     them for white made them read heavier than their dark-mode counterparts —
     tan became orange, lavender became a hard purple — so these sit only as
     far down as a 3px bar needs to be seen, keeping the softer character the
     dark theme has. Decorative only: nothing here carries text. */
  --ok-line: #2fae71;
  --warn-line: #d69a2e;
  --info-line: #4a90e8;
  --note-line: #9268e8;
  --bad-line: #e05f5f;
  /* Was #4a8fd8, which was four degrees of hue from the default card edge and
     two from Blue -- so "Steel" was not a colour anybody could see they had
     chosen. Teal is 42 degrees off the default and 29 off Green. */
  --cool-line: #2fbfae;
  --lime-line: #b6d94a;
  --pink-line: #e86ac6;
  --dim-line: #9aa3b0;

  /* Muted text was sitting too light against these pale grounds. */
  --muted: #4d5766;
  --muted-deep: #5f6875;
}

/* Document folders. A heading and its own table, so a folder reads as a
   container rather than as a sort order. */
.doc-folder { font-size:.88rem; margin:1.1rem 0 .35rem; display:flex; gap:.75rem;
  align-items:baseline; justify-content:space-between; }
.doc-folder:first-of-type { margin-top:.4rem; }
.doc-folder .muted { font-weight:400; font-size:.8rem; }
.doc-empty { font-size:.82rem; margin:.1rem 0 .6rem; }

/* Positions, and how much room a settled one deserves. A filled position is
   done with; it stays on the list but steps back so the gaps carry the weight.
   Only the colour and scale change — no row is hidden by being filled, because
   hiding is what the Unfilled toggle is for. */
.crew-filled > td { color: var(--muted); }
.crew-filled > td:first-child strong { font-weight: 500; }
.crew-open-l { color: var(--warn-fg); font-weight: 600; }
.crew-toggle { display: inline-flex; gap: 0; }
.crew-toggle .btn { border-radius: 0; }
.crew-toggle .btn:first-child { border-radius: 7px 0 0 7px; }
.crew-toggle .btn:last-child { border-radius: 0 7px 7px 0; margin-left: -1px; }
.crew-toggle .btn.is-on { background: var(--accent-2); border-color: var(--accent-2); color: #fff; }

/* The two-column gear picker: what we own on the left, where it is going on
   the right. Both columns scroll on their own so the page itself does not —
   dragging across a page that is also scrolling is miserable. */
.pick-grid { display:grid; grid-template-columns:1fr 1fr; gap:1.25rem; align-items:start; }
@media (max-width: 900px) { .pick-grid { grid-template-columns:1fr; } }
.pick-col { display:flex; flex-direction:column; max-height:calc(100vh - 210px); }
.pick-filters { display:flex; flex-direction:column; gap:.4rem; margin:.2rem 0 .7rem; }
.pick-filters input, .pick-filters select { font-size:.85rem; }
/* The form between the card and its list has to pass the height down. Left as
   a plain block it was content-height and unbounded, so the list sized itself
   against that instead of against the card's cap and hung off the page.
   min-height:0 is the half that matters: without it a flex item refuses to
   shrink below its content, and the list pushes rather than scrolls. */
.pick-form { display:flex; flex-direction:column; flex:1; min-height:0; }
.pick-list { overflow-y:auto; flex:1; min-height:220px; padding-right:.2rem; }
.pick-none { font-size:.85rem; margin:.4rem 0 0; }

.pick-item { border:1px solid var(--border); border-radius:7px; padding:.4rem .55rem;
  margin-bottom:.35rem; cursor:grab; background:var(--panel-2); display:flex;
  flex-direction:column; gap:.1rem; }
.pick-item:hover { border-color:var(--border-strong); }
.pick-item.dragging { opacity:.45; }
.pick-name { font-size:.86rem; font-weight:600; display:flex; gap:.4rem; align-items:center; flex-wrap:wrap; }
.pick-meta { font-size:.74rem; }

.pick-room { border:1px solid var(--border); border-radius:9px; padding:.5rem .6rem;
  margin-bottom:.6rem; background:var(--panel-2); }
.pick-room-head { display:flex; justify-content:space-between; align-items:baseline;
  gap:.5rem; font-size:.88rem; margin-bottom:.4rem; }
.pick-drop { border:1px dashed var(--border); border-radius:7px; padding:.35rem .5rem;
  margin-bottom:.3rem; background:var(--panel); }
.pick-drop.drop-on { border-style:solid; border-color:var(--accent-2);
  outline:2px solid var(--accent-2); outline-offset:1px; }
.pick-drop-room { border-style:solid; border-color:var(--border); }
.pick-drop-l { font-size:.82rem; display:flex; gap:.45rem; align-items:baseline; flex-wrap:wrap; }
.pick-time { font-family:var(--mono); font-size:.74rem; color:var(--muted); }
.pick-holds { display:flex; gap:.3rem; flex-wrap:wrap; margin-top:.3rem; }
.pick-holds:empty { margin-top:0; }
.pick-chip { display:inline-flex; align-items:center; gap:.25rem; font-size:.74rem;
  background:var(--info-bg); color:var(--info-fg); border:1px solid var(--info-bd);
  border-radius:99px; padding:.05em .2em .05em .55em; }
.pick-x { background:none; border:0; color:inherit; cursor:pointer; font-size:.95em;
  line-height:1; padding:0 .25em; opacity:.7; }
.pick-x:hover { opacity:1; }
.pick-msg { position:fixed; left:50%; bottom:1.2rem; transform:translateX(-50%);
  z-index:80; background:var(--ok-bg); color:var(--ok-fg); border:1px solid var(--ok-bd);
  border-radius:8px; padding:.45rem .9rem; font-size:.85rem; margin:0; }
.pick-msg.is-bad { background:var(--bad-bg); color:var(--bad-fg); border-color:var(--bad-bd); }

/* Gear, grouped by where it is coming from. */
.gear-group { font-size:.85rem; margin:1.1rem 0 .3rem; display:flex; gap:.5rem;
  align-items:baseline; letter-spacing:.01em; }
.gear-group:first-of-type { margin-top:.6rem; }
.gear-group .muted { font-weight:400; font-size:.78rem; }

.autosave-note { margin-left: .6rem; font-size: .8rem; }
.autosave-note.is-bad { color: var(--red); }

/* Collapsing the boxes on a page. The heading stays, everything under it
   goes; the card keeps its own border so the page still reads as a stack of
   things rather than a list of orphaned titles. */
.card.is-shut > *:not(h2):not(.card-head) { display: none; }
.card.is-shut { padding-bottom: .6rem; }
/* The title is normally a direct child, but a card that sets its title beside
   a row of totals wraps the pair in a header. Both are the card's own title
   and both get the pointer and the caret. */
.card > h2, .card > .card-head { cursor: pointer; }

/* One corner, every card. Floated inside the title the arrow landed wherever
   that title ended -- the right edge on a card whose title owns the row, the
   middle of the row on one that shares it with a dial, a chip or a pair of
   buttons. Anchored to the card instead, it takes no part in the header's
   layout and cannot be moved by it. */
.card > h2::after, .card > .card-head::after {
  content: '▾';
  position: absolute; right: 1.25rem; top: 1.05rem;
  font-size: 1rem; line-height: 1.35; opacity: .8; color: var(--muted);
  transition: transform .15s, color .15s; transform-origin: center;
}
/* Room reserved beside it, so a long title or a row of buttons never runs
   underneath the arrow. */
.card > h2, .card > .card-head { padding-right: 1.6rem; }
.card > h2:hover::after, .card > .card-head:hover::after { color: var(--accent-2); opacity: 1; }
.card.is-shut > h2::after,
.card.is-shut > .card-head::after { transform: rotate(-90deg); }

/* A folder inside Documents folds like a card, and says so the same way. */
.doc-group.is-shut > *:not(.doc-folder) { display: none; }
.doc-folder { cursor: pointer; position: relative; padding-right: 1.6rem; }
.doc-folder::after {
  content: '▾'; position: absolute; right: 0; top: 0;
  font-size: .9em; opacity: .7; color: var(--muted);
  transition: transform .15s, color .15s; transform-origin: center;
}
.doc-folder:hover::after { color: var(--accent-2); opacity: 1; }
.doc-group.is-shut > .doc-folder::after { transform: rotate(-90deg); }
/* Only when it stands on its own beside the heading. Script puts it in the
   controls row where there is already a gap, and the margin there made it the
   one button that did not line up with the rest. */
.page-head > .collapse-all { margin-left: .5rem; }

/* The theme switch sits in the account menu and has to read as one of its
   rows, not as a form that wandered in. */
.navmenu-theme { margin: 0; }
.navmenu-theme button {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; font: inherit; color: inherit;
  padding: .5rem .8rem;
}
.navmenu-theme button:hover { background: var(--tint-3); }

/* A card needs a lift on light, where a border alone reads as a wireframe. */
:root[data-theme="light"] .card { box-shadow: var(--shadow); }
/* A room column is a container, not a card; on light it needs an edge of its
   own or the columns dissolve into the card behind them. */
:root[data-theme="light"] .agenda-room { border-color: #0f172a1f; }
:root[data-theme="light"] .agenda-session { box-shadow: var(--shadow); }
/* Empty states were the palest thing on the page and read as broken.
   --muted-deep is the quieter of the two, so this takes --muted. */
:root[data-theme="light"] .agenda-empty,
:root[data-theme="light"] .agenda-unassigned .muted { color: var(--muted); }
/* On dark this button is a dim outline; on light it needs a real fill.
   Scoped away from the variants that bring their own: a theme selector is
   three components strong and would otherwise beat .btn-primary on one,
   painting a white background under white text and erasing the button. */
:root[data-theme="light"] .btn:not(.btn-primary):not(.btn-danger) {
  background: var(--panel); box-shadow: var(--shadow);
}
:root[data-theme="light"] .btn:not(.btn-primary):not(.btn-danger):hover { background: var(--panel-2); }
/* Fields sit below the card they are in, so they read as cut into it. */
:root[data-theme="light"] input:not([type=checkbox]):not([type=radio]),
:root[data-theme="light"] select,
:root[data-theme="light"] textarea { background: var(--panel); }
:root[data-theme="light"] .card input:not([type=checkbox]):not([type=radio]),
:root[data-theme="light"] .card select,
:root[data-theme="light"] .card textarea { background: var(--bg); }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 'Geist', "Helvetica Neue", "Segoe UI", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* Faint grid texture on both axes. */
  background-image:
    linear-gradient(var(--grid) 1px, #0000 1px),
    linear-gradient(90deg, var(--grid) 1px, #0000 1px);
  background-size: 64px 64px, 64px 64px;
  background-attachment: fixed;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 1.5rem; margin: 0 0 .25rem; letter-spacing: -.02em; font-weight: 650; }
h2 { font-size: 1.05rem; margin: 0 0 .75rem; letter-spacing: -.01em; font-weight: 600; }
code, .mono { font-family: var(--mono); }
code { background: var(--panel-2); padding: .1em .4em; border-radius: 4px; font-size: .85em; }
/* Figures read as Geist Mono, per the brand. */
.num, .stat-n, .money, td.num, th.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
small, .muted { color: var(--muted); }
em { color: var(--muted); }
::selection { background: #7b2fd644; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Skip link. Parked off-screen rather than display:none, because a hidden
   element is not in the tab order at all and this one has to be the first thing
   in it. It becomes visible only when focused, so nothing changes for anyone
   using a mouse. */
.skip {
  position: absolute; left: .5rem; top: -3rem; z-index: 200;
  background: var(--panel); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 8px;
  padding: .55em 1em; font-size: .9rem; font-weight: 600;
  transition: top .12s ease;
}
.skip:focus { top: .5rem; text-decoration: none; }
/* The jump target takes focus programmatically; it should not then draw a ring
   around the whole page. */
main[tabindex="-1"]:focus { outline: none; }

@media (prefers-reduced-motion: reduce) {
  .skip { transition: none; }
}

/* ============================ Navigation ============================
   One bar. Five groups plus Overview, each opening a menu, with messages and
   the account tucked to the right. Replaces the old arrangement where a top
   nav and a fifteen-tab sub-nav duplicated each other.
   ================================================================== */
.topbar {
  display: flex; align-items: center; gap: .35rem;
  padding: .55rem 1.1rem;
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  position: sticky; top: 0; z-index: 40;
}
.topbar::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: var(--accent-bar); opacity: .5;
}
.brand {
  display: inline-flex; align-items: center; gap: .55rem;
  font-weight: 600; font-size: 1rem; color: var(--text);
  letter-spacing: -.01em; white-space: nowrap; margin-right: 1rem;
}
.brand-mark { display: block; height: 14px; width: auto; }
.brand-word b {
  font-weight: 700;
  background: var(--accent-bar); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.brand:hover { text-decoration: none; }
/* Back — first thing in the bar, so it is where a thumb already is. */
.backbtn {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .35rem .55rem; margin-right: .1rem;
  border: 1px solid var(--border); border-radius: 8px;
  color: var(--muted); font-size: .82rem; font-weight: 600; line-height: 1;
}
.backbtn:hover { text-decoration: none; color: var(--text); border-color: var(--accent-2); }
@media (max-width: 900px) { .backbtn-l { display: none; } .backbtn { padding: .4rem .5rem; } }

.mainnav { display: flex; align-items: center; gap: .15rem; }

/* Top-level entries: links and menu buttons look identical. */
.nav-top {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .44em .8em; border-radius: 8px;
  color: var(--muted); font-size: .9rem; font-weight: 500;
  white-space: nowrap; cursor: pointer; user-select: none;
  border: 1px solid transparent;
  transition: color .14s, background-color .14s;
}
.nav-top:hover { color: var(--text); background: var(--tint-4); text-decoration: none; }
.nav-top.nav-on { color: var(--text); background: var(--tint-5); border-color: var(--border); }
/* The active group carries the brand ramp as a short underline. */
.nav-top.nav-on::after {
  content: ''; position: absolute; left: .8em; right: .8em; bottom: -.5rem; height: 2px;
  background: var(--accent-bar); border-radius: 2px;
}
.navgroup > summary.nav-top, .mainnav > a.nav-top { position: relative; }

.navgroup { position: relative; }
.navgroup > summary { list-style: none; }
.navgroup > summary::-webkit-details-marker { display: none; }
.nav-caret { opacity: .55; transition: transform .18s; flex: none; }
.navgroup[open] > summary .nav-caret { transform: rotate(180deg); }
.navgroup[open] > summary.nav-top { color: var(--text); background: var(--tint-5); }

/* The dropdown panel. */
.navmenu {
  position: absolute; top: calc(100% + .55rem); left: 0; z-index: 50;
  min-width: 262px; padding: .4rem;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 26px 60px -24px #000000d9, 0 0 0 1px var(--tint-2) inset;
  animation: navpop .16s ease-out;
}
.navmenu-right { left: auto; right: 0; }
@keyframes navpop { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: none; } }

/* Log out is a button in a form now -- signing out changes state, so it is a
   POST -- and it has to be indistinguishable from the links beside it. */
.navmenu a,
.navmenu button.navmenu-out {
  display: block; padding: .5rem .65rem; border-radius: 9px;
  color: var(--text); position: relative;
}
.navmenu button.navmenu-out {
  width: 100%; text-align: left; background: none; border: none;
  font: inherit; cursor: pointer;
}
.navmenu-outform { margin: 0; }
.navmenu a:hover,
.navmenu button.navmenu-out:hover { background: var(--tint-4); text-decoration: none; }
/* A short ramp bar marks the row you are hovering or already on. */
.navmenu a:hover::before, .navmenu a.navmenu-on::before {
  content: ''; position: absolute; left: 0; top: .55rem; bottom: .55rem; width: 2px;
  background: var(--accent-bar); border-radius: 2px;
}
.navmenu a.navmenu-on { background: var(--tint-3); }
.navmenu-l { display: block; font-size: .89rem; font-weight: 500; }
.navmenu-h { display: block; font-size: .76rem; color: var(--muted); margin-top: .1rem; line-height: 1.35; }
.navmenu-head {
  padding: .5rem .65rem .55rem; margin-bottom: .25rem;
  border-bottom: 1px solid var(--border);
  font-size: .89rem; font-weight: 600;
}
.navmenu-head .muted { display: block; font-size: .74rem; font-weight: 400; margin-top: .05rem; }
.navmenu-out { margin-top: .25rem; border-top: 1px solid var(--border); border-radius: 0 0 9px 9px; }
.navmenu-out .navmenu-l { color: var(--muted); }
.navmenu-out:hover .navmenu-l { color: var(--red); }
.navmenu-out:hover::before { display: none; }

/* Right side: messages + account. */
.userbox { margin-left: auto; display: flex; align-items: center; gap: .4rem; }
.msgbtn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .44em .75em; border-radius: 8px;
  color: var(--muted); font-size: .9rem; font-weight: 500; position: relative;
}
.msgbtn:hover { color: var(--text); background: var(--tint-4); text-decoration: none; }
.msgbtn.nav-on { color: var(--text); background: var(--tint-5); }
.unread {
  min-width: 1.25rem; padding: 0 .32em; height: 1.25rem;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-bar); color: #fff;
  font-size: .7rem; font-weight: 700; font-family: var(--mono);
  border-radius: 99px;
}
.userchip {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .3em .55em .3em .3em; border-radius: 99px;
  cursor: pointer; user-select: none; list-style: none;
  border: 1px solid var(--border); color: var(--muted); font-size: .88rem;
  transition: color .14s, background-color .14s;
}
.userchip::-webkit-details-marker { display: none; }
.userchip:hover, .navgroup[open] > .userchip { color: var(--text); background: var(--tint-4); }
.avatar {
  width: 25px; height: 25px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-gradient); color: #fff;
  font-size: .68rem; font-weight: 700; letter-spacing: .02em;
}

/* Mobile: the bar collapses to a drawer. */
.nav-burger {
  display: none; margin-left: auto;
  width: 38px; height: 34px; padding: 8px 9px;
  background: none; border: 1px solid var(--border); border-radius: 9px; cursor: pointer;
  flex-direction: column; justify-content: space-between;
}
.nav-burger span { display: block; height: 1.5px; background: var(--text); border-radius: 2px; }

@media (max-width: 900px) {
  .topbar { gap: .5rem; padding: .55rem .9rem; flex-wrap: wrap; }
  .nav-burger { display: flex; order: 3; }
  .userbox { order: 2; margin-left: auto; }
  .msgbtn-l { display: none; }
  .userchip-n { display: none; }

  .mainnav {
    order: 4; flex-basis: 100%;
    display: none; flex-direction: column; align-items: stretch; gap: .1rem;
    padding: .5rem 0 .25rem; margin-top: .4rem;
    border-top: 1px solid var(--border);
  }
  .mainnav.is-open { display: flex; }
  .nav-top { justify-content: space-between; padding: .6em .7em; font-size: .95rem; }
  .nav-top.nav-on::after { display: none; }
  .nav-top.nav-on { background: var(--tint-5); }
  /* Inline the menus rather than floating them over a narrow screen. */
  .navmenu {
    position: static; min-width: 0; box-shadow: none; animation: none;
    background: var(--tint-1); border-radius: 10px; margin: .15rem 0 .35rem;
  }
  .navmenu-right { right: auto; }
  body.nav-locked { overflow: hidden; }
}

.wrap { flex: 1; width: 100%; max-width: 1100px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem; }
.foot { text-align: center; color: var(--muted); font-size: .8rem; padding: 1rem; border-top: 1px solid var(--border); }

/* In-page tab strip (reports view switcher). Not the site nav — that is the
   grouped bar above; this switches views within a single page. */
.subnav { display: flex; gap: .25rem; margin: -0.25rem 0 1.25rem; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.subnav a { padding: .5em 1em; color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -1px; }
.subnav a:hover { color: var(--text); text-decoration: none; }
.subnav .subnav-on {
  color: var(--text); font-weight: 600; border-bottom-color: transparent;
  background: var(--accent-bar) bottom left / 100% 2px no-repeat;
}

/* Settings overview: one card per section, each stating where it stands.
   auto-fit rather than a fixed column count, so the row fills at any width and
   no card is left stretched across dead space on its own. */
.set-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 1rem; align-items: stretch; }
.set-grid > .card { margin-bottom: 0; display: flex; flex-direction: column; }
.set-grid h2 { font-size: 1.05rem; margin-bottom: .25rem; }
/* Pushed to the bottom so the state lines up across cards whose blurbs wrap
   to different heights. */
.set-grid .set-state { margin-top: auto; padding-top: .6rem; }

.set-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.set-list li { padding-left: 1rem; position: relative; }
.set-list li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 5px; height: 5px; border-radius: 50%; background: var(--muted);
}

/* Crew portal identity line (my.php / my-show.php). */
.who { color: var(--muted); font-size: .9rem; }
a.who:hover { color: var(--text); text-decoration: none; }

/* Cards & layout */
.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  margin-bottom: 1.25rem;
  position: relative;          /* the collapse arrow anchors to this corner */
}
/* flex-end rather than space-between, working with the auto margin below.
   space-between only groups the controls while everything fits on one line;
   the moment a long title pushes them onto a second row it spreads them to
   opposite ends of it, which is how Solutions ended up with + Add Solution
   at the left and Collapse all at the right. */
.page-head { display: flex; justify-content: flex-end; align-items: flex-start; gap: 1rem; margin-bottom: 1.25rem; flex-wrap: wrap; }
/* The controls at the end of a page head belong beside each other, not spread
   across the width. An auto margin on the title block soaks up the slack
   before justify-content gets a say, so everything after it packs to the
   right -- which is also what the Rate Card meant by right-justify both.
   Held to the wide layout: below the stacking breakpoint the main axis is
   vertical and a horizontal auto margin would fight align-items: stretch. */
@media (min-width: 641px) {
  /* The title keeps the left edge -- an auto margin beats justify-content, so
     it stays put even on a row of its own -- and everything after it packs to
     the right, together, whether or not it wrapped to get there. */
  .page-head > *:first-child { margin-right: auto; }
}
.crumb { margin: 0 0 .25rem; font-size: .85rem; color: var(--muted); }
.applinks { display: flex; gap: 1rem; align-items: center; flex-wrap: wrap; margin: .4rem 0 0; font-size: .9rem; }
.cols { display: grid; grid-template-columns: 3fr 2fr; gap: 1.25rem; align-items: start; }
.col-side .card, .col-main .card { margin-bottom: 1.25rem; }
@media (max-width: 860px) { .cols { grid-template-columns: 1fr; } }
/* Equal-height columns (dashboard feeds): both cards match the taller one */
.cols-even { align-items: stretch; }
.cols-even > .card { margin-bottom: 0; }

/* Equal-height columns where each side is a STACK of cards (app page):
   the taller column leads; the shorter column's last card grows to match. */
.cols-match { align-items: stretch; }
.cols-match > .col-main, .cols-match > .col-side { display: flex; flex-direction: column; }
.cols-match > .col-main > .card:last-child,
.cols-match > .col-side > .card:last-child { flex: 1 0 auto; margin-bottom: 0; }

/* Masonry card wall: auto-fits columns to width and balances heights so cards
   pack tight with no big gaps. Cards never split across columns. */
.cardwall { columns: 21rem; column-gap: 1.25rem; }
.cardwall > .card { break-inside: avoid; -webkit-column-break-inside: avoid; }

/* Pager */
.pager { display: flex; gap: .3rem; flex-wrap: wrap; align-items: center; margin-top: .9rem; }
.pager a, .pager span { padding: .18em .5em; border-radius: 6px; font-size: .8rem; border: 1px solid var(--border); line-height: 1; }
.pager a { color: var(--accent); }
.pager a:hover { border-color: var(--accent-2); text-decoration: none; }
.pager .current { background: var(--accent-2); border-color: var(--accent-2); color: #fff; }
.pager .disabled { color: var(--muted); opacity: .45; }
.pager .ellipsis { border: none; padding: .18em .15em; color: var(--muted); }

/* Badges */
.badge {
  display: inline-block; font-size: .72rem; font-weight: 600;
  padding: .12em .6em; border-radius: 99px; letter-spacing: .02em;
  border: 1px solid transparent; white-space: nowrap;
}
.st-live     { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.st-beta     { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.st-dev      { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.st-archived { background: var(--off-bg); color: var(--off-fg); border-color: var(--off-bd); }
.k-feature   { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
.k-function  { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.k-fix       { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.cs-proposed    { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.cs-approved    { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.cs-in_progress { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.cs-done        { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.cs-rejected    { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.kd-desktop     { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.emp-freelance  { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.emp-fulltime   { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
/* Part-time is an employee too -- same family as full-time, dimmer, because
   the thing worth spotting at a glance is who is on the payroll at all. */
.emp-parttime   { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); opacity: .82; }
.as-tentative   { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.as-confirmed   { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.sh-inquiry     { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.sh-quoted      { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.sh-confirmed   { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.sh-completed   { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
.sh-cancelled   { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.iv-draft       { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.iv-sent        { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.iv-paid        { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.iv-void        { background: var(--off-bg); color: var(--off-fg); border-color: var(--off-bd); }
.q-draft        { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.q-sent         { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.q-accepted     { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.q-declined     { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.cl-lead        { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.cl-active      { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.rq-open        { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.rq-converted   { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.rq-declined    { background: var(--off-bg); color: var(--off-fg); border-color: var(--off-bd); }
.cl-contacted   { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.cl-negotiating { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.cl-licensed    { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
.cl-sold        { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.cl-passed      { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.prep-pending   { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.prep-pulled    { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.prep-packed    { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.prep-out       { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
.prep-returned  { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.po-draft       { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.po-ordered     { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.po-confirmed   { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.po-received    { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.ct-added   { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }
.ct-fixed   { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.ct-changed { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.ct-removed { background: var(--off-bg); color: var(--off-fg); border-color: var(--off-bd); }

/* Buttons & forms */
.btn {
  display: inline-block; cursor: pointer;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--border-control); border-radius: 7px;
  padding: .45em 1em; font-size: .9rem; font-family: inherit;
  /* Stated, not inherited: a <button> takes the UA's 'normal' line-height while
     a <summary> inherits the page's, so the same class rendered two heights
     depending on which element wore it. */
  line-height: 1.55;
}
/* Hover has to step up from the resting edge, not down to the decorative one. */
.btn:hover { border-color: var(--border-control-hi); text-decoration: none; }
.btn-primary {
  background: var(--accent-gradient); background-size: 140% 100%;
  border-color: transparent; color: #fff; font-weight: 550;
  transition: background-position .25s, filter .15s;
}
.btn-primary:hover { background-position: 100% 0; filter: brightness(1.08); border-color: transparent; }
.btn-danger { background: transparent; border-color: var(--bad-bd); color: var(--red); }
.btn-danger:hover { border-color: var(--red); }
.btn-small { padding: .25em .7em; font-size: .8rem; }
.linklike {
  background: none; border: none; padding: 0; cursor: pointer;
  color: var(--muted); font-size: .8rem; font-family: inherit; text-decoration: underline;
}
.linklike:hover { color: var(--red); }

summary.btn::-webkit-details-marker { display: none; }
summary.btn { list-style: none; user-select: none; }
.reveal > form, .reveal > .card { margin-top: .75rem; }
.reveal .card { margin-bottom: 0; }

/* align-items:end is what keeps the boxes level.
   A label fills its grid row and its contents flow from the top, so a caption
   that takes two lines -- a required badge, a hint, a long name that wraps --
   pushes its own control down while the one beside it stays put. Aligning to
   the end of the row pins each label's last child, which is always its control,
   to the same bottom edge; the caption grows upward instead. */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; margin-bottom: 1rem;
  align-items: end; }
.form-grid .span2 { grid-column: span 2; }
/* A label is a block, not a column flex, so its caption sits on the same line
   as the field name. As a column flex every <small> took a line of its own,
   which made that label taller and dropped its box below the one beside it —
   the offset boxes. The control is forced to its own line underneath. */
.form-grid label { display: block; font-size: .85rem; color: var(--muted); }
.form-grid label > input,
.form-grid label > select,
.form-grid label > textarea,
.form-grid label > span:not(.req) { display: block; margin-top: .25rem; }
/* …except a tick box, which belongs beside its words, not above them. */
.form-grid label > input[type=checkbox],
.form-grid label > input[type=radio] { display: inline-block; margin-top: 0; }
.form-grid label > small { margin-left: .35rem; }
input, select, textarea {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border-control); border-radius: 7px;
  box-shadow: var(--control-inset);
  padding: .5em .7em; font: inherit; font-size: .92rem; width: 100%;
  /* Stated, not inherited. A <select> sizes its box from its own internal
     metric and an <input> from the line box, so the same padding produced a
     40px input beside a 39px select -- one pixel, but it is the pixel that
     makes a row of fields look hand-placed. */
  line-height: 1.4;
  /* And one box height for both. A native <select> adds internal padding an
     <input> does not, so identical padding still rendered a 37px input beside a
     39px select -- bottoms level, tops two pixels apart. Stating the height
     settles it without stripping the select's own arrow. */
  min-height: 2.5rem;
}
textarea { min-height: 0; }
input[type=checkbox], input[type=radio], input[type=file] { min-height: 0; }
/* A tick box is the size of a tick box. Left on width:100% it became a flex
   item as wide as the row, which pushed its own caption onto the next line —
   the floating checkbox with its label stranded underneath it. */
input[type=checkbox], input[type=radio] {
  width: auto; min-width: 0; padding: 0; flex: none; vertical-align: middle;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent-2); }
textarea { resize: vertical; }

/* Date and time fields open their picker from a click anywhere in the box
   (see assets/forms.js), so the whole control reads as clickable. */
input[type=date], input[type=time], input[type=month],
input[type=week], input[type=datetime-local] { cursor: pointer; }
input[type=date]::-webkit-calendar-picker-indicator,
input[type=time]::-webkit-calendar-picker-indicator,
input[type=month]::-webkit-calendar-picker-indicator,
input[type=week]::-webkit-calendar-picker-indicator,
input[type=datetime-local]::-webkit-calendar-picker-indicator {
  cursor: pointer; opacity: .5; padding-left: .3em;
  transition: opacity .15s;
}
input[type=date]:hover::-webkit-calendar-picker-indicator,
input[type=time]:hover::-webkit-calendar-picker-indicator,
input[type=month]:hover::-webkit-calendar-picker-indicator,
input[type=week]:hover::-webkit-calendar-picker-indicator,
input[type=datetime-local]:hover::-webkit-calendar-picker-indicator { opacity: 1; }
/* A button sits at the top of its cell. Stretched, it grew to match whatever
   tall field landed beside it — a Save the height of a notes box. */
.form-grid .btn { justify-self: start; align-self: start; width: auto; }

/* Lists */
.list, .timeline, .feed { list-style: none; margin: 0; padding: 0; }
.list-row { padding: .7rem 0; border-top: 1px solid var(--border); }
.list-row:first-child { border-top: none; }
.row-title { font-weight: 600; color: var(--text); }
.row-title:hover { color: var(--accent); }
.row-meta { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; font-size: .82rem; margin-top: .2rem; }
.row-meta.big { margin: 0 0 1.25rem; font-size: .9rem; }
.timeline li { padding: .7rem 0; border-top: 1px solid var(--border); }
.timeline li:first-child { border-top: none; }
.timeline .body { margin-top: .35rem; font-size: .92rem; }
.feed li { padding: .45rem 0; border-top: 1px solid var(--border); font-size: .88rem; }
.feed li:first-child { border-top: none; }
.feed-time { color: var(--muted); font-size: .78rem; display: inline-block; min-width: 70px; }
.inline { display: inline; }
.inline-details { display: inline-block; position: relative; }
.inline-details summary { list-style: none; cursor: pointer; }
/* small inline forms (e.g. reset password): a compact flex row */
.inline-details form:not(.form-grid) { margin-top: .3rem; display: flex; gap: .4rem; }
.inline-details form:not(.form-grid) input { width: 160px; }
/* bigger edit forms open as a contained popover instead of stretching the cell */
.inline-details > form.form-grid,
.inline-details > .card {
  position: absolute; top: 1.4rem; right: 0; z-index: 30;
  width: 340px; max-width: 82vw;
  box-shadow: 0 12px 32px rgba(0,0,0,.5);
}
.inline-details > form.form-grid { grid-template-columns: 1fr 1fr; }
.inline-details > .card { text-align: left; }
@media (max-width: 640px) {
  .inline-details > form.form-grid,
  .inline-details > .card { right: auto; left: 0; }
}

/* A row's own columns keep their width whatever is open in the Actions cell.
   The portal panel used to sit in the flow and push, which broke "Manager"
   across two lines mid-word in a column that had room for it a moment before. */
.table td.actions { white-space: nowrap; }

/* Two-column split inside a full-width card (socials | assets) */
.split { display: grid; grid-template-columns: 1fr 1fr; gap: 1.75rem; }
.split-col > h2:first-child, .split-col > h3:first-child { margin-top: 0; }
.split-col + .split-col { border-left: 1px solid var(--border); padding-left: 1.75rem; }
@media (max-width: 720px) {
  .split { grid-template-columns: 1fr; gap: 1.25rem; }
  .split-col + .split-col { border-left: none; padding-left: 0; border-top: 1px solid var(--border); padding-top: 1.25rem; }
}

/* Stat tiles (events overview) */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1rem; margin-bottom: 1.25rem; }
.stat {
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: .9rem 1.1rem;
  position: relative; overflow: hidden;
}
/* A short accent rule keys each tile to the brand without shouting. */
.stat::before {
  content: ''; position: absolute; top: 0; left: 0; width: 34%; height: 2px;
  background: var(--accent-bar);
}
.stat-n { font-size: 1.35rem; font-weight: 700; letter-spacing: -.02em; }
.stat-l { color: var(--muted); font-size: .82rem; margin-top: .15rem; }

/* Quote builder */
.qsection { padding: .9rem 0; border-top: 1px solid var(--border); }
.qsection:first-child { border-top: none; padding-top: 0; }
.qsection-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
.qsection h2 { margin-bottom: .4rem; }
.qtable td, .qtable th { padding: .35rem .5rem; }
.num { text-align: right; white-space: nowrap; }
.qadd { margin-top: .6rem; }
.taxlabel { display: flex !important; flex-direction: row !important; align-items: center; gap: .45rem !important; }
.taxlabel input { width: auto; display: inline-block !important; margin-top: 0 !important; }
.grand td { border-top: 2px solid var(--accent-2); font-size: 1.02rem; }
.internal-block { margin-top: 1rem; border-top: 1px dashed var(--border); }
.internal-title { font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; padding-top: .7rem; }
.gain { color: var(--green); }
.loss { color: var(--red); }
.conflict-row { background: rgba(224,93,93,.08); }
.conflict-note { color: var(--red); font-size: .78rem; margin-top: .2rem; }

/* Labor grid (spreadsheet) */
.lgrid th, .lgrid td { padding: .25rem .3rem; }
.lgrid-day { text-align: center; border-left: 2px solid var(--border); font-size: .78rem; }
.lgrid-sub { text-align: center; color: var(--muted); font-size: .7rem; font-weight: 600; }
.lgrid input { padding: .3em .35em; }
.lgrid input.hr { background: var(--bg); text-align: center; }
.prepost { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; margin-bottom: 1rem; font-size: .9rem; }
.prepost label { flex-direction: row; align-items: center; gap: .35rem; }
.lgrid-pre  { background: rgba(232,180,90,.07); }
.lgrid-post { background: rgba(157,123,234,.07); }
.lgrid-day.lgrid-pre  { border-top: 2px solid var(--warn-fg); }
.lgrid-day.lgrid-post { border-top: 2px solid #9d7bea; }
.lgrid-tag { font-size: .58rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
/* Amber here too — a company holiday is not a cancelled day. */
.lgrid-holiday { background: rgba(232,180,90,.10); }
.lgrid-day.lgrid-holiday { border-top: 2px solid var(--holiday); }
.lgrid-star { color: var(--holiday); }
.lgrid-dark { background: repeating-linear-gradient(45deg, rgba(120,120,140,.05), rgba(120,120,140,.05) 6px, rgba(120,120,140,.12) 6px, rgba(120,120,140,.12) 12px); }

/* Crew availability schedule */
.sched { border-collapse: collapse; font-size: .8rem; min-width: 100%; }
.sched th, .sched td { border: 1px solid var(--border); padding: .3rem .35rem; text-align: center; }
.sched-name { text-align: left !important; white-space: nowrap; position: sticky; left: 0; background: var(--panel); z-index: 1; }
.sched thead th { color: var(--muted); font-weight: 600; }
.sched-weekend { background: rgba(255,255,255,.02); }
.sched-today { outline: 2px solid var(--accent); outline-offset: -2px; }
.sched-cell { height: 34px; min-width: 40px; }
.sched-book { display: block; color: #fff; border-radius: 4px; padding: .1em .3em; font-size: .68rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sched-book:hover { text-decoration: none; opacity: .9; }
/* A month is thirty-odd columns. The cells get tighter and the booking label
   shrinks to a bar of colour — the shape of who is busy is the point at this
   width; the name is in the tooltip and one click away. */
.sched-month { font-size: .72rem; }
.sched-month th, .sched-month td { padding: .25rem .15rem; }
.sched-month .sched-cell { min-width: 22px; height: 30px; }
/* No label fits in a 22px column, so the bar is pure colour. Its height must
   be stated in pixels: em padding on a font-size:0 element computes to zero,
   which is how every booking vanished in month view. */
.sched-month .sched-book { font-size: 0; line-height: 0; padding: 0; height: 16px; border-radius: 3px; }
.sched-tools { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
.sched-toggle { display: inline-flex; gap: .25rem; }
.sched-toggle .btn.is-on { background: var(--accent-2); border-color: var(--accent-2); color: #fff; }
.sched-jump { display: flex; gap: .4rem; align-items: center; }
.sched-jump label { display: flex; gap: .35rem; align-items: center; font-size: .78rem; color: var(--muted); white-space: nowrap; }
.sched-jump input[type=date] { min-width: 0; width: 9.5rem; }
.sched-filter { display: flex; gap: .6rem .9rem; align-items: end; flex-wrap: wrap; margin-bottom: 1rem; }
.sched-filter > label { display: block; font-size: .78rem; color: var(--muted); min-width: 0; }
.sched-filter > label > input[type=search] { display: block; margin-top: .2rem; }
.sched-filter .cf-grow { flex: 1 1 16rem; }
.sched-count { font-size: .82rem; margin-left: auto; }
/* Gear availability cells */
.gav-cell { font-weight: 600; }
.gav-n { display: inline-block; min-width: 1.4em; }
.gav-free { }
.gav-part { background: var(--ok-bg); color: var(--ok-fg); }
.gav-full { background: var(--warn-bg); color: var(--warn-fg); }
.gav-over { background: var(--bad-bg2); color: var(--bad-fg2); }
.gav-sub  { background: var(--note-bg); color: var(--note-fg); }
.gav-over-key { color: var(--bad-fg2); font-weight: 600; }
.gav-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; vertical-align: middle; border: 1px solid var(--border); }
.gav-swatch.gav-free { background: var(--panel); }
/* Reports */
.rep-controls { display: flex; flex-wrap: wrap; gap: 1rem; align-items: center; justify-content: space-between; }
.rep-presets { display: flex; gap: .4rem; flex-wrap: wrap; }
.rep-range { display: flex; gap: .6rem; align-items: flex-end; flex-wrap: wrap; }
.rep-range label { font-size: .8rem; color: var(--muted); display: flex; flex-direction: column; gap: .2rem; }
.rep-views { margin-top: 1rem; }
.rep-window { margin: .6rem 0 1rem; font-size: .85rem; }
.rep-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: .8rem; margin-bottom: 1.2rem; }
.rep-stat { background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px; padding: .9rem 1rem; }
.rep-stat-v { font-size: 1.35rem; font-weight: 700; }
.rep-stat-l { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-top: .2rem; }
.rep-stat.pos .rep-stat-v { color: var(--ok-fg); }
.rep-stat.neg .rep-stat-v { color: var(--bad-fg); }
.rep-stat.warn .rep-stat-v { color: var(--warn-fg); }
.rep-table th, .rep-table td { white-space: nowrap; }
.rep-pos { color: var(--ok-fg); }
.rep-neg { color: var(--bad-fg); }
.rep-warn { color: var(--warn-fg); }
.rep-total td { border-top: 2px solid var(--border); font-weight: 700; }
.rep-age { display: inline-block; padding: .05em .5em; border-radius: 10px; font-size: .72rem; font-weight: 600; }
.rep-age-cur { background: var(--ok-bg); color: var(--ok-fg); }
.rep-age-b30 { background: var(--warn-bg); color: var(--warn-fg); }
.rep-age-b60 { background: var(--warn-bg); color: var(--warn-fg); }
.rep-age-b90 { background: var(--bad-bg2); color: var(--bad-fg2); }

/* Calendar */
.cal { display: grid; /* minmax(0,1fr) not 1fr: a plain 1fr is minmax(auto,1fr), so a column cannot shrink below its content and the nowrap event titles held the grid open past the screen. With 0 the titles ellipsis instead. */
  grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.cal-dow { background: var(--panel-2); color: var(--muted); font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; text-align: center; padding: .4rem 0; }
.cal-cell { background: var(--panel); min-width: 0; min-height: 92px; padding: .3rem; display: flex; flex-direction: column; gap: 2px; }
.cal-empty { background: var(--bg); }
.cal-day { font-size: .78rem; color: var(--muted); }
.cal-today { outline: 2px solid var(--accent); outline-offset: -2px; }
.cal-today .cal-day { color: var(--accent); font-weight: 700; }
/* The bar is the show's own hue; the left edge is what the colour used to
   mean. Lightness is set here rather than in the markup because white text
   needs a darker ground on the light theme than it does on the dark one. */
.cal-ev {
  font-size: .72rem; padding: .12em .4em; border-radius: 4px; color: #fff;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: hsl(var(--ev-h, 210) 42% 38%);
  border-left: 3px solid rgba(0,0,0,.35);
}
:root[data-theme="light"] .cal-ev { background: hsl(var(--ev-h, 210) 46% 42%); }
/* A run of days reads as one bar: only the ends are rounded, and the middle
   loses the gap that made every day look like a separate booking. */
.cal-ev-first { border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: -2px; }
.cal-ev-last  { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -2px; }
.cal-ev-mid   { border-radius: 0; margin-left: -2px; margin-right: -2px; }
/* A continuing day carries no name -- nine truncated copies of one title is
   what made two shows hard to tell apart to begin with. */
.cal-ev-cont { display: inline-block; }
/* An empty line holding a slot open, so the band above it stays straight. It
   is a .cal-ev too, so its height and spacing cannot drift from a real bar's. */
.cal-ev-gap { visibility: hidden; background: none; border-left-color: transparent; }
.cal-ev:hover { text-decoration: none; opacity: .9; }
.cal-legend { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: .75rem; font-size: .8rem; color: var(--muted); }
.cal-key { display: inline-flex; align-items: center; gap: .35rem; }
.cal-swatch { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }
/* In the legend the status swatch is the shape it is on a bar: an edge. */
.cal-swatch-edge { width: 4px; height: 12px; border-radius: 2px; }
.cal-key-note { color: var(--muted); }
/* On a bar these colour the left edge; in the legend they fill the swatch. */
.cal-ev.calst-inquiry   { border-left-color: #9aa4b4; }
.cal-ev.calst-quoted    { border-left-color: #5b96e8; }
.cal-ev.calst-confirmed { border-left-color: #46c97b; }
.cal-ev.calst-completed { border-left-color: #a488e8; }
.cal-ev.calst-cancelled { border-left-color: #e0696d; }
.cal-swatch.calst-inquiry   { background: #9aa4b4; }
.cal-swatch.calst-quoted    { background: #5b96e8; }
.cal-swatch.calst-confirmed { background: #46c97b; }
.cal-swatch.calst-completed { background: #a488e8; }
.cal-swatch.calst-cancelled { background: #e0696d; }
/* A cancelled show should not read as a healthy block of colour. */
.cal-ev.calst-cancelled { opacity: .55; text-decoration: line-through; }
/* A holiday is not a cancellation. Red is spoken for in the legend, so the
   star is amber, and the day says which holiday it is rather than making you
   hover to find out. */
.cal-holiday .cal-day { color: var(--holiday); font-weight: 700; }
.cal-star { color: var(--holiday); }
.cal-holname {
  font-size: .64rem; line-height: 1.25; color: var(--warn-fg);
  text-transform: uppercase; letter-spacing: .04em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cal-ev.cal-dark { opacity: .6; text-decoration: line-through; }
@media (max-width: 640px) { .cal-cell { min-height: 68px; } .cal-ev { font-size: .62rem; } }

/* Filter chips (clients) */
.chips { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 1.25rem; }
.chip { display: inline-flex; gap: .4rem; align-items: center; padding: .3em .75em; border-radius: 99px;
  border: 1px solid var(--border); background: var(--panel); color: var(--muted); font-size: .85rem; }
.chip:hover { border-color: var(--accent-2); text-decoration: none; color: var(--text); }
.chip strong { color: var(--text); }
.chip-on { border-color: var(--accent-2); color: var(--text); background: var(--panel-2); }

/* Rows for inactive/retired records (crew, gear, rates, solutions, venues…) */
.is-archived { opacity: .55; }

/* Table */
.table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.table th { text-align: left; color: var(--muted); font-weight: 600; font-size: .8rem; padding: .4rem .6rem; border-bottom: 1px solid var(--border); }
.table td { padding: .55rem .6rem; border-bottom: 1px solid var(--border); vertical-align: top; }
/* The actions cell has to stay a table cell. `display:flex` here took it out of
   the table layout, so it no longer stretched to the row height and its bottom
   border drew short of the divider on every other column. Laying the links out
   inline-block keeps the spacing and the wrapping without costing the cell its
   place in the row. */
.table td.actions > * { display: inline-block; vertical-align: top; margin-right: .8rem; }
.table td.actions > *:last-child { margin-right: 0; }

/* Flash + auth */
.flash { padding: .6rem 1rem; border-radius: 7px; margin-bottom: 1rem; font-size: .9rem; }
.flash-ok { background: var(--ok-bg); color: var(--ok-fg); border: 1px solid var(--ok-bd); }
.flash-error { background: var(--bad-bg); color: var(--bad-fg); border: 1px solid var(--bad-bd); }
.auth-card {
  max-width: 400px; margin: 8vh auto 0;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 2rem;
  position: relative; overflow: hidden;
  box-shadow: 0 24px 60px -30px #0258d366;
}
/* Brand ramp capping the sign-in card. */
.auth-card::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--accent-bar);
}
.auth-card h1 { text-align: center; margin-bottom: 1rem; }
.auth-card h1 span { color: var(--accent); }
.auth-brand { letter-spacing: -.02em; font-weight: 600; }
.auth-brand span {
  font-weight: 700;
  background: var(--accent-bar); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.auth-card form { display: flex; flex-direction: column; gap: .8rem; }
.auth-card label { display: flex; flex-direction: column; gap: .25rem; font-size: .85rem; color: var(--muted); }
.auth-card .btn { margin-top: .5rem; }

/* ============================ Sales pipeline (Kanban) ============================ */
.pipe-stats { margin-bottom: 1rem; }
.kboard { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 10px; align-items: flex-start; }
/* Columns share the available width so all stages fit without horizontal scroll;
   they only scroll as a fallback on very narrow screens. */
.kcol { flex: 1 1 0; min-width: 158px; background: var(--panel-2); border: 1px solid var(--border); border-top: 3px solid var(--border); border-radius: 10px; display: flex; flex-direction: column; min-height: 130px; }
.kstage-inquiry   { border-top-color: var(--info-fg); }
.kstage-quoted    { border-top-color: var(--warn-fg); }
.kstage-confirmed { border-top-color: var(--ok-fg); }
.kstage-completed { border-top-color: var(--note-fg); }
.kstage-cancelled { border-top-color: var(--bad-fg); }
.kcol-head { display: flex; justify-content: space-between; align-items: center; padding: .55rem .7rem; border-bottom: 1px solid var(--border); }
.kcol-title { font-weight: 700; font-size: .85rem; }
.kcol-meta { font-size: .72rem; color: var(--muted); white-space: nowrap; }
.kcol-body { padding: .5rem; display: flex; flex-direction: column; gap: .5rem; min-height: 60px; flex: 1; }
.kcol.kover { outline: 2px dashed var(--accent); outline-offset: -3px; }
.kcol-empty { color: var(--muted); font-size: .74rem; text-align: center; padding: 1rem .5rem; border: 1px dashed var(--border); border-radius: 8px; }
.kcard { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: .55rem .6rem; cursor: grab; position: relative; }

/* A visually-hidden label: read by a screen reader, never drawn. Not
   display:none, which would take it out of the accessibility tree as well. */
.vh {
  position: absolute !important; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden; white-space: nowrap;
  clip: rect(0 0 0 0); clip-path: inset(50%); border: 0;
}

/* The keyboard and single-pointer way to move a deal. Absolutely positioned so
   a card is exactly the height it always was, and revealed on hover or when
   anything inside the card takes focus -- so it is reachable by tabbing and by
   a pointer that cannot drag, and invisible to everyone else. */
.kcard-move {
  position: absolute; right: .35rem; bottom: .3rem; margin: 0;
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.kcard:hover .kcard-move,
.kcard:focus-within .kcard-move { opacity: 1; pointer-events: auto; }
/* Add without dragging: one destination above the list, a small button on each
   row. Positioned over the row so nothing changes height, and revealed on hover
   or focus so it is reachable by tab and by a pointer that cannot drag. */
.rail-chip, .pick-item { position: relative; }
.rail-target { display: block; margin: 0 0 .5rem; font-size: .78rem; color: var(--muted); }
.rail-target select { width: auto; max-width: 100%; margin-top: .2rem; font-size: .8rem; }
.chip-add {
  position: absolute; right: .3rem; bottom: .22rem;
  font: inherit; font-size: .68rem; line-height: 1.4;
  padding: .1em .5em; border-radius: 6px; cursor: pointer;
  background: var(--panel-2); color: var(--text); border: 1px solid var(--border-control);
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.rail-chip:hover .chip-add, .rail-chip:focus-within .chip-add,
.pick-item:hover .chip-add, .pick-item:focus-within .chip-add,
.chip-add:focus-visible { opacity: 1; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) { .chip-add { transition: none; } }
@media (hover: none) { .chip-add { opacity: 1; pointer-events: auto; } }

.kcard-move { display: flex; gap: .25rem; align-items: center; }
.kcard-move select {
  width: auto; font-size: .7rem; padding: .1em 1.2em .1em .4em;
  background: var(--panel-2); border-color: var(--border-control);
}
/* The confirm sits beside the menu rather than over the card, so it does not
   cover the card's own text while the menu is open. */
.kcard-go { position: static; opacity: inherit; pointer-events: inherit; }
@media (prefers-reduced-motion: reduce) { .kcard-move { transition: none; } }
/* Touch has no hover, so leave it visible rather than unreachable. */
@media (hover: none) { .kcard-move { opacity: 1; pointer-events: auto; } }
.kcard:active { cursor: grabbing; }
.kcard.kdrag { opacity: .45; }
.kcard-title { font-weight: 600; font-size: .86rem; display: block; color: var(--text); }
.kcard-title:hover { text-decoration: underline; }
.kcard-client { font-size: .75rem; color: var(--muted); margin-top: 1px; }
.kcard-val { font-weight: 700; margin-top: .35rem; font-size: .88rem; }
.kcard-prob { font-size: .7rem; color: var(--accent); font-weight: 600; margin-left: .2rem; }
.kcard-foot { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .4rem; font-size: .68rem; color: var(--muted); align-items: center; }
.kcard-src { background: var(--panel-2); border: 1px solid var(--border); border-radius: 10px; padding: .05em .5em; }
.kcard-task { background: var(--ok-bg); color: var(--ok-fg); border-radius: 10px; padding: .05em .5em; }
.kcard-task.kcard-overdue { background: var(--bad-bg2); color: var(--bad-fg2); }
.kcard-lost { font-size: .7rem; color: var(--bad-fg); margin-top: .35rem; font-style: italic; }

/* ============================ CRM activity timeline ============================ */
.act-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
.act-form { margin-top: .5rem; }
.act-tasks { margin: .7rem 0; display: flex; flex-direction: column; gap: .45rem; }
.act-todo { display: flex; gap: .6rem; align-items: flex-start; background: var(--panel-2); border: 1px solid var(--border); border-radius: 8px; padding: .5rem .6rem; }
.act-todo.act-overdue { border-color: var(--bad-bd); background: var(--bad-bg); }
.act-check { background: none; border: none; color: var(--muted); font-size: 1.15rem; cursor: pointer; padding: 0; line-height: 1; }
.act-check:hover { color: var(--ok-fg); }
.act-todo-body { flex: 1; }
.act-meta { font-size: .72rem; color: var(--muted); margin-top: .2rem; }
.act-when { color: var(--warn-fg); font-weight: 600; }
.act-overdue .act-when { color: var(--bad-fg2); }
.act-del { margin-left: auto; }
.act-list { list-style: none; margin: .7rem 0 0; padding: 0; }
.act-list li { display: flex; gap: .6rem; align-items: flex-start; padding: .55rem 0; border-top: 1px solid var(--border); }
.act-list li:first-child { border-top: none; }
.act-body { flex: 1; }
.act-done { opacity: .55; }
.act-kind { flex: none; }
.act-note    { background: var(--neut-bg); color: var(--neut-fg); border-color: var(--neut-bd); }
.act-call    { background: var(--info-bg); color: var(--info-fg); border-color: var(--info-bd); }
.act-email   { background: var(--note-bg); color: var(--note-fg); border-color: var(--note-bd); }
.act-meeting { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }
.act-task    { background: var(--ok-bg); color: var(--ok-fg); border-color: var(--ok-bd); }

/* ============================ Proposal / brand studio ============================ */
.opt-badge { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.qline-opt td { background: rgba(232,180,90,.05); }
.qline-actions { display:flex; gap:.6rem; white-space:nowrap; }
.copyfield { width:100%; padding:.5em .6em; border:1px solid var(--border); border-radius:8px; background:var(--panel-2); color:var(--text); font:inherit; font-size:.82rem; }
.proposal-actions { display:flex; gap:.5rem; align-items:center; margin-top:.6rem; flex-wrap:wrap; }
.proposal-actions select, .proposal-actions input { flex:1; min-width:0; }

/* ============================ Contracts & e-sign ============================ */
.ct-draft  { background:var(--neut-bg); color:var(--neut-fg); border-color:var(--neut-bd); }
.ct-sent   { background:var(--info-bg); color:var(--info-fg); border-color:var(--info-bd); }
.ct-signed { background:var(--ok-bg); color:var(--ok-fg); border-color:var(--ok-bd); }
.ct-void   { background:var(--off-bg); color:var(--off-fg); border-color:var(--off-bd); }
.contract-edit { width:100%; font:13px/1.5 ui-monospace,"Cascadia Code",Consolas,monospace; }
.contract-body { white-space:pre-wrap; font:14px/1.6 -apple-system,"Segoe UI",sans-serif; background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:1rem 1.1rem; max-height:520px; overflow:auto; }
.sig-img { max-width:240px; max-height:90px; background:#fff; border-radius:6px; padding:4px; margin:.4rem 0; display:block; }
.audit td { padding:.25rem .5rem; font-size:.85rem; }
.linklike.danger { color:var(--red); }

/* ============================ Kits & consumables ============================ */
.gk-kit        { background:var(--note-bg); color:var(--note-fg); border-color:var(--note-bd); }
.gk-consumable { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.kit-block { border:1px solid var(--border); border-radius:10px; padding:.8rem 1rem; margin-bottom:.9rem; background:var(--panel-2); }
.kit-head { display:flex; justify-content:space-between; align-items:flex-start; gap:1rem; }
.kit-components { margin:.6rem 0; }
.kit-components td { padding:.3rem .5rem; }
.stock-low { color:var(--red); font-weight:700; }

/* ============================ Maintenance & lifecycle ============================ */
.mt-oos  { background:var(--bad-bg2); color:var(--bad-fg2); border-color:var(--bad-bd2); }
.mt-due  { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.mt-ok   { background:var(--ok-bg); color:var(--ok-fg); border-color:var(--ok-bd); }
.mt-open { background:var(--info-bg); color:var(--info-fg); border-color:var(--info-bd); }
.mk-service    { background:var(--ok-bg); color:var(--ok-fg); border-color:var(--ok-bd); }
.mk-repair     { background:var(--bad-bg2); color:var(--bad-fg2); border-color:var(--bad-bd2); }
.mk-inspection { background:var(--info-bg); color:var(--info-fg); border-color:var(--info-bd); }
.mt-actions { display:flex; gap:.7rem; align-items:center; flex-wrap:wrap; }

/* ============================ Asset tracking ============================ */
.ast-in          { background:var(--ok-bg); color:var(--ok-fg); border-color:var(--ok-bd); }
.ast-out         { background:var(--info-bg); color:var(--info-fg); border-color:var(--info-bd); }
.ast-maintenance { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.ast-staged      { background:var(--info-bg); color:var(--info-fg); border-color:var(--info-bd); }
.ast-transit     { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.ast-lost        { background:var(--bad-bg2); color:var(--bad-fg2); border-color:var(--bad-bd2); }
.ast-retired     { background:var(--off-bg); color:var(--off-fg); border-color:var(--off-bd); }
.asset-filter { display:flex; gap:.4rem; flex-wrap:wrap; margin-bottom:1rem; }
.asset-lost-row td { background:rgba(224,93,93,.06); }
.asset-history { list-style:none; margin:0; padding:0; font-size:.85rem; }
.asset-history li { padding:.3rem 0; border-top:1px solid var(--border); }
.asset-history li:first-child { border-top:none; }
/* Scan station */
.scan-box { max-width:640px; }
.scan-form { display:flex; gap:.6rem; }
.scan-form input { flex:1; font-size:1.3rem; padding:.6em .8em; border:2px solid var(--accent-2); border-radius:10px; background:var(--panel-2); color:var(--text); font-family:ui-monospace,Consolas,monospace; }
.scan-miss { margin-top:1rem; padding:.9rem 1rem; background:var(--bad-bg); color:var(--bad-fg); border-radius:10px; }
.scan-card { margin-top:1.2rem; border:1px solid var(--border); border-radius:12px; padding:1.1rem 1.2rem; background:var(--panel-2); }
.scan-tag { font-size:1.2rem; }
.scan-name { font-size:1.05rem; font-weight:600; margin:.2rem 0; }
.scan-actions { display:flex; gap:.6rem; align-items:center; flex-wrap:wrap; margin-top:1rem; }
.scan-checkout { display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; }
.scan-checkout select { padding:.5em .6em; border:1px solid var(--border); border-radius:8px; background:var(--panel); color:var(--text); }
.btn-lg { font-size:1.05rem; padding:.7em 1.4em; }

/* ============================ Camera scanner overlay ============================ */
.cam-overlay { position:fixed; inset:0; z-index:1000; background:#000; display:flex; align-items:center; justify-content:center; }
.cam-overlay video { width:100%; height:100%; object-fit:cover; position:absolute; inset:0; }
.cam-frame { position:relative; width:72%; max-width:360px; aspect-ratio:1.6/1; border:3px solid #fff; border-radius:14px; box-shadow:0 0 0 100vmax rgba(0,0,0,.45); }
.cam-hint { position:absolute; bottom:64px; left:50%; transform:translateX(-50%); color:#fff; background:rgba(0,0,0,.55); padding:.5em 1.1em; border-radius:20px; font-size:.9rem; z-index:2; }
/* Offered only where the camera has a lamp. Sits opposite Close so a thumb
   holding the phone one-handed can reach it. */
.cam-torch { position:absolute; top:16px; left:16px; background:rgba(255,255,255,.92); color:#111; border:none; border-radius:8px; padding:.5em 1.1em; font:inherit; font-weight:600; cursor:pointer; z-index:2; }
.cam-torch.is-on { background:#ffd34d; }
.cam-close { position:absolute; top:16px; right:16px; background:rgba(255,255,255,.92); color:#111; border:none; border-radius:8px; padding:.5em 1.1em; font:inherit; font-weight:600; cursor:pointer; z-index:2; }

/* Case packing box */
.pack-box { margin-top:1.2rem; border-top:1px solid var(--border); padding-top:1rem; }
.pack-box h3 { font-size:.95rem; margin:0 0 .5rem; }

/* Gear filter + asset chips */
.gear-filter { display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; margin-bottom:.8rem; }
.gear-filter input[type=search] { flex:1; min-width:180px; padding:.5em .7em; border:1px solid var(--border); border-radius:8px; background:var(--panel-2); color:var(--text); font:inherit; }
.gear-filter select { padding:.5em .6em; border:1px solid var(--border); border-radius:8px; background:var(--panel-2); color:var(--text); }
.asset-chip { background:var(--info-bg); color:var(--info-fg); border:1px solid var(--info-bd); border-radius:5px; padding:.05em .4em; font-size:.78rem; }

/* PO procurement panel */
.po-proc { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:1rem; }
.po-proc-block { border:1px solid var(--border); border-radius:10px; padding:.8rem .9rem; }
.po-proc-lbl { font-size:.78rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted); margin-bottom:.5rem; }

/* RFQ */
.rfqst-open    { background:var(--warn-bg); color:var(--warn-fg); border-color:var(--warn-bd); }
.rfqst-awarded { background:var(--ok-bg); color:var(--ok-fg); border-color:var(--ok-bd); }
.rfqst-closed  { background:var(--off-bg); color:var(--off-fg); border-color:var(--off-bd); }
.rfq-best td { background:rgba(63,185,107,.08); }

/* ============================ Scheduling board ============================ */
.board-msg { padding:.7rem 1rem; border-radius:8px; margin-bottom:1rem; font-weight:600; }
.board-msg.ok   { background:var(--ok-bg); color:var(--ok-fg); }
.board-msg.warn { background:var(--warn-bg); color:var(--warn-fg); }
.board-msg.err  { background:var(--bad-bg); color:var(--bad-fg); }
.board-scroll { overflow-x:auto; }
.board-grid { display:grid; grid-template-columns:180px repeat(var(--cols), minmax(34px,1fr)); gap:4px; min-width:100%; align-items:center; }
.board-corner { }
.board-day { text-align:center; font-size:.7rem; color:var(--muted); padding:.2rem 0; border-radius:6px; }
.board-day.is-weekend { background:rgba(255,255,255,.03); }
.board-day.is-today { background:var(--accent-2); color:#fff; font-weight:700; }
.board-daynum { font-size:.85rem; color:var(--text); }
.board-rowlabel { font-size:.85rem; padding:.4rem .2rem; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.board-track { display:grid; grid-template-columns:repeat(var(--cols), minmax(34px,1fr)); gap:4px; }
.board-bar { border-radius:8px; padding:.45rem .6rem; color:#fff; font-size:.78rem; overflow:hidden; cursor:pointer; border:2px solid transparent; min-height:44px; }
.board-bar-name { display:block; font-weight:700; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.board-bar-meta { display:block; opacity:.9; font-size:.7rem; }
.board-bar.drop-on { border-color:#fff; box-shadow:0 0 0 3px rgba(79,156,249,.5); }
/* A one or two day show gets a bar barely wider than its padding, so its name
   was ellipsised away to nothing. Short bars keep the block as the drop target
   and hang the name beside it, flipping to the left near the right-hand edge so
   it stays on screen. The meta line is dropped — the title tooltip carries it. */
.board-scroll { overflow-y: visible; }
.board-bar.is-short { position:relative; overflow:visible; padding:.45rem .25rem; }
.board-bar.is-short .board-bar-meta { display:none; }
.board-bar.is-short .board-bar-name {
  position:absolute; top:50%; left:calc(100% + 6px); transform:translateY(-50%);
  background:var(--panel-2); border:1px solid var(--border); border-radius:6px;
  padding:.1rem .4rem; font-size:.72rem; font-weight:600; color:var(--text);
  max-width:170px; pointer-events:none; z-index:3;
}
.board-bar.is-short.label-left .board-bar-name { left:auto; right:calc(100% + 6px); }
/* Colour is the show; status is the stripe down its left edge. The same rule
   the calendar follows, so a show is recognisably itself in both places -- and
   a board of confirmed shows is no longer one wall of green. --ev-h comes off
   the show id, so it is stable for the life of that show. */
.board-bar { background: hsl(var(--ev-h, 210) 42% 34%); border-left: 3px solid transparent; }
:root[data-theme="light"] .board-bar { background: hsl(var(--ev-h, 210) 46% 40%); }
.sh-bar-inquiry   { border-left-color:#9aa4b4; }
.sh-bar-quoted    { border-left-color:#5b96e8; }
.sh-bar-confirmed { border-left-color:#46c97b; }
.sh-bar-completed { border-left-color:#a488e8; }
/* Resource rails */
.rail { display:flex; flex-wrap:wrap; gap:.5rem; max-height:280px; overflow-y:auto; }
.rail-chip { border:1px solid var(--border); background:var(--panel-2); border-radius:9px; padding:.4rem .6rem; font-size:.82rem; cursor:grab; display:flex; gap:.4rem; align-items:center; }
.rail-chip:active { cursor:grabbing; }
.rail-chip.dragging { opacity:.45; }
.rail-chip.is-oos { opacity:.5; cursor:not-allowed; }
/* Vendor-quote review: a row whose arithmetic does not close, and kit
   components that are already paid for by their parent line. */
tr.row-bad > td { background: color-mix(in srgb, var(--danger, #e0483f) 9%, transparent); }
tr.row-comp > td { opacity: .72; font-size: .92em; }
tr.row-comp input, tr.row-comp select { background: transparent; }

/* Quantity to book when the chip is dragged onto a show. */
.chip-qty { margin-left:auto; display:flex; align-items:center; gap:.15rem; color:var(--muted); font-size:.75rem; cursor:default; }
.chip-qty input { width:3.1rem; padding:.1rem .2rem; font-size:.75rem; text-align:center; cursor:text; }
.suggest-chip { display:inline-flex; gap:.35rem; align-items:center; border:1px solid var(--border); border-radius:20px; padding:.15em .6em; margin:.15rem .3rem .15rem 0; font-size:.8rem; }
.suggest-chip.is-busy { opacity:.6; }

/* ============================ Venues & agenda ============================ */
.venue-know { padding:.6rem 0; border-top:1px solid var(--border); }
.venue-know:first-child { border-top:none; }
.venue-know-l { font-size:.72rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); margin-bottom:.2rem; }
/* Day strip */
.agenda-days { display:flex; gap:.4rem; overflow-x:auto;
  padding:9px 3px .5rem; margin:-9px -3px 1rem; }
.agenda-day { flex:none; display:flex; flex-direction:column; align-items:center; gap:1px; min-width:64px;
  padding:.5rem .6rem; border:1px solid var(--border); border-radius:9px; background:var(--panel-2); color:var(--text); position:relative; }
.agenda-day:hover { text-decoration:none; border-color:var(--accent-2); }
.agenda-day.is-on { background:var(--accent-2); border-color:var(--accent-2); color:#fff; }
.agenda-day-d { font-size:.68rem; text-transform:uppercase; letter-spacing:.06em; opacity:.8; }
.agenda-day-n { font-size:.88rem; font-weight:700; }
.agenda-day-c { position:absolute; top:-6px; right:-6px; background:var(--accent); color:#fff; font-size:.62rem;
  font-weight:700; border-radius:20px; padding:.1em .4em; }
/* Room columns */
.agenda-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:.9rem; }
.agenda-room { border:1px solid var(--border); border-radius:10px; background:var(--panel-2); padding:.6rem; }
.agenda-room-head { display:flex; justify-content:space-between; align-items:center; gap:.5rem;
  padding-bottom:.5rem; margin-bottom:.5rem; border-bottom:1px solid var(--border); font-size:.9rem; }
.agenda-empty { font-size:.82rem; text-align:center; padding:.8rem 0; margin:0; }
.agenda-session { background:var(--panel); border:1px solid var(--border); border-left:3px solid var(--accent-2);
  border-radius:8px; padding:.5rem .6rem; margin-bottom:.5rem; }
.agenda-time { font-size:.74rem; color:var(--muted); font-variant-numeric:tabular-nums; }
.agenda-title { font-weight:600; font-size:.92rem; margin:.1rem 0; }
.agenda-meta { display:flex; gap:.4rem; align-items:center; flex-wrap:wrap; font-size:.75rem; margin-top:.25rem; }
.agenda-notes { font-size:.78rem; color:var(--muted); margin-top:.3rem; }
.agenda-actions { display:flex; gap:.6rem; margin-top:.4rem; font-size:.78rem; }
.agenda-unassigned { margin-top:1.2rem; padding-top:.9rem; border-top:1px dashed var(--border); }
.agenda-unassigned h3 { font-size:.9rem; margin:0 0 .5rem; }
.agenda-addroom { display:flex; gap:1rem; flex-wrap:wrap; margin-top:1rem; padding-top:.9rem; border-top:1px solid var(--border); }
/* Above the grid the rule belongs underneath, not on top of it. */
/* Defaults editors: a field, its value, and the sentence that says what the
   value does. Wide enough that the explanation is readable, not a tooltip. */
.rule-grid { display:grid; grid-template-columns:repeat(auto-fit, minmax(230px, 1fr));
  gap:1rem 1.4rem; margin:.9rem 0 1rem; }
.rule-field { display:flex; flex-direction:column; gap:.3rem; min-width:0; }
.rule-field label { font-size:.82rem; font-weight:600; }
.rule-field input { width:100%; }
.rule-field small { font-size:.76rem; line-height:1.35; }
.rule-field.is-pending label { color:var(--muted); }
.rule-flag { display:inline-block; margin-left:.35rem; padding:.05rem .35rem;
  border:1px solid var(--border); border-radius:3px; font-size:.66rem;
  font-weight:500; text-transform:uppercase; letter-spacing:.03em; vertical-align:middle; }
/* The show defaults sit inside the edit form's grid, so they need to stop
   inheriting the field spacing around them. */
/* Timesheets: a row per logged day, wide enough to carry the bands and the
   planned-vs-actual comparison without any cell being free to grow. */
/* A folded room on the gear picker keeps its name and date and drops the rest.
   The head is a button so it is reachable from the keyboard; it has to be told
   not to look like one. */
.pick-room-head {
  display: flex; gap: .5rem; align-items: baseline; width: 100%;
  background: none; border: 0; padding: 0; margin: 0 0 .4rem;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.pick-room-head::after {
  content: '\25be'; margin-left: auto; font-size: .7em; opacity: .45;
  transition: transform .15s; transform-origin: center;
}
.pick-room.is-shut > .pick-room-head { margin-bottom: 0; }
.pick-room.is-shut > .pick-room-head::after { transform: rotate(-90deg); }
.pick-room.is-shut > *:not(.pick-room-head) { display: none; }

/* A product in a list: its icon, then its name, linking to its own site. The
   monogram stands in for the products whose sites do not publish an icon. */
.prod { display: inline-flex; align-items: center; gap: .45rem; }
.prod-icon { flex: none; width: 18px; height: 18px; border-radius: 4px; object-fit: contain; }
.prod-mono {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--tint-7); color: var(--accent-2);
  font-size: .68rem; font-weight: 700; line-height: 1;
}
a.prod { text-decoration: none; }
a.prod:hover span:last-child { text-decoration: underline; }

/* Document folders now nest one level. The child is indented and set slightly
   quieter than its parent, so the tree reads at a glance without needing lines
   drawn between the levels. */
.doc-folder-child { margin-left: 1.4rem; font-size: .88em; opacity: .92; }
/* The marker goes on the name, not on the row. The row is a flex container
   that pushes its children apart, so a ::before on the row became a third item
   stranded at the far left while the name sat out in the middle. */
.doc-folder-child > span:first-child::before { content: '\21b3'; margin-right: .4rem; opacity: .5; }
/* The controls belong beside the name they act on rather than flung to the
   far edge of a wide card. */
.doc-folder > span:first-child { margin-right: auto; }
.doc-folder .doc-rename, .doc-folder form { flex: none; }
.doc-rename { display: inline-block; }
.doc-rename > summary { display: inline; }
.doc-rename-form { display: inline-flex; gap: .4rem; align-items: center; margin-left: .5rem; }
.doc-rename-form input { max-width: 12rem; }

/* The labour grid's call, end of day and meal belong to one day at a time, so
   they say which, and the hours they produce are marked as worked out rather
   than typed. */
.lgrid-daypick { display: flex; gap: .8rem; align-items: center; flex-wrap: wrap; margin: 0 0 1rem; font-size: .85rem; }
.lgrid-daypick label { display: flex; gap: .4rem; align-items: center; }
.lgrid-forday { display: block; font-weight: 400; font-size: .68rem; color: var(--muted); letter-spacing: 0; }
.lgrid-callday { white-space: nowrap; }
.lgrid-derived input { background: var(--tint-3); font-style: italic; cursor: not-allowed; }

/* ---- Fulfilling planner (fill.php) --------------------------------------
   Three panes: the positions, the people, and whoever is being considered.
   They collapse to one column early. The documented rule for this app is that
   nothing may shrink-lock the layout open, and three columns of names and
   badges reach that point long before a phone does. */
.fill-panes { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: 1rem; align-items: start; }
@media (max-width: 1100px) { .fill-panes { grid-template-columns: 1fr 1fr; } }
@media (max-width: 760px)  { .fill-panes { grid-template-columns: 1fr; } }
.fill-pane { min-width: 0; }
.fill-h { font-size: .82rem; text-transform: uppercase; letter-spacing: .06em;
          color: var(--muted); margin: 1rem 0 .4rem; font-weight: 600; }
.fill-h:first-of-type { margin-top: .4rem; }
.fill-none { font-size: .85rem; margin: .2rem 0 .4rem; }
.fill-note { font-size: .85rem; }
.fill-target { font-size: .85rem; }
.fill-who { font-size: 1rem; text-transform: none; letter-spacing: 0; color: var(--text); }

.fill-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .3rem; }

.fill-pos, .fill-tech {
  display: flex; align-items: center; gap: .5rem;
  border: 1px solid var(--border); border-radius: 8px; padding: .45rem .6rem;
  background: var(--panel-2);
}
.fill-pos-hit, .fill-tech-hit {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .1rem;
  text-decoration: none; color: inherit;
}
.fill-pos-hit:hover, .fill-tech-hit:hover { text-decoration: none; }
.fill-pos:hover, .fill-tech:hover { border-color: var(--accent-2); }
.fill-pos-role, .fill-tech-name { font-weight: 600; font-size: .9rem; }
.fill-pos-meta, .fill-tech-meta { font-size: .78rem; }
.fill-rate { font-weight: 400; font-size: .78rem; margin-left: .35rem; }
.fill-flags { display: flex; gap: .3rem; flex-wrap: wrap; margin-top: .15rem; }

/* The position being filled, and the person being read about. */
.fill-pos.is-sel, .fill-tech.is-sel { border-color: var(--accent); background: var(--tint-6); }
.fill-pos.is-open { border-left: 3px solid var(--warn-line); }
.fill-pos.is-no { border-left: 3px solid var(--bad-line); }
.fill-tag { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
            color: var(--accent-2); white-space: nowrap; }
.fill-clear, .fill-assign { flex: none; }

/* Somebody already working elsewhere stays in the list -- sometimes you move
   them -- but must not look like an ordinary choice. */
.fill-tech.has-clash { opacity: .72; }
.fill-tech.has-clash .fill-tech-name { text-decoration: none; }

.fill-tech[draggable=true] { cursor: grab; }
.fill-tech.dragging { opacity: .45; cursor: grabbing; }
.fill-pos.drop-on { border-color: var(--accent); background: var(--tint-7);
                    box-shadow: 0 0 0 2px var(--accent-2) inset; }

/* Sixty technicians is longer than the other two panes put together, and a
   pane that drags the page down leaves the position list and the profile
   stranded at the top of a screen of nothing. The list scrolls inside itself
   instead, which is what makes this three panes rather than three lists. */
.fill-techs { max-height: 62vh; overflow-y: auto; padding-right: .3rem; }
@media (max-width: 760px) { .fill-techs { max-height: none; overflow-y: visible; } }

.fill-filters { display: flex; gap: .7rem; align-items: flex-end; flex-wrap: wrap; margin: .3rem 0 .8rem; }
.fill-filters label { display: flex; flex-direction: column; gap: .2rem; font-size: .78rem; }
.fill-filters > label:not(.fill-check) { flex: 1 1 7rem; min-width: 0; }
/* The two switches and the button belong together on their own line rather
   than trailing off the end of whichever field happened to fit. */
.fill-filters .fill-check:first-of-type { margin-left: 0; }
.fill-check { flex: 0 0 auto; flex-direction: row !important; align-items: center; gap: .35rem !important; }
.fill-check input { margin: 0; }

/* The two arrows that move a room earlier or later in the grid. Deliberately
   quiet -- they sit beside edit and the remove cross in a header that already
   has three things in it -- and they come forward on hover. */
.room-move { display:inline-flex; gap:.1rem; align-items:center; margin-left:auto; }
.room-move button { padding:0 .18rem; font-size:.78rem; line-height:1; opacity:.45; }
.room-move button:hover, .room-move button:focus-visible { opacity:1; }
.agenda-room-head:hover .room-move button { opacity:.8; }
.agenda-room-head .room-move + .inline-details { margin-left:.35rem; }

.fill-warn { border: 1px solid var(--bad-line); border-radius: 8px; padding: .5rem .7rem;
             margin: .6rem 0; font-size: .85rem; background: var(--tint-6); }
.fill-warn ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.fill-warn li { margin: .15rem 0; }

/* By person: a roster to pick from, and one person's year beside it.
   Same two-pane shape as the fulfilling planner, because it is the same
   gesture -- pick somebody on the left, read them on the right. */
/* The shelf somebody is standing at. A banner rather than a field, because it
   is a mode the whole page is in until they say they are done. */
.scan-held { display:flex; gap:1rem; align-items:center; flex-wrap:wrap; justify-content:space-between;
             border:1px solid var(--accent); border-left-width:3px; border-radius:10px;
             padding:.7rem .9rem; margin-bottom:1rem; background:var(--tint-6); }
.scan-held code { font-size:.8rem; }
/* The show picker sits where the held banner would, and is the same height and
   the same inset, so starting a load-out does not make the page jump. */
.scan-pickshow { display:flex; gap:.6rem; align-items:center; flex-wrap:wrap;
                 border:1px solid var(--line); border-left-width:3px; border-radius:10px;
                 padding:.7rem .9rem; margin-bottom:1rem; }
.scan-pickshow select { width:auto; min-width:14rem; }
/* An exception, so it should read as one rather than competing with the button
   above it. */
.scan-elsewhere { margin-top:.5rem; }
.scan-elsewhere summary { cursor:pointer; font-size:.85rem; }
.scan-elsewhere .scan-checkout { display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; margin-top:.5rem; }
/* A shelf reads as sitting inside the rack above it. */
tr.bin-shelf td:first-child { padding-left:1.1rem; }
.bin-in { color:var(--muted); }
tr.bin-rack td:first-child strong { font-size:1rem; }

.per-panes { display:grid; grid-template-columns: minmax(210px, 280px) 1fr; gap:14px; align-items:start; }
@media (max-width: 900px) { .per-panes { grid-template-columns: 1fr; } }
.per-pane { max-height: 78vh; overflow-y:auto; }
.per-list { list-style:none; margin:0; padding:0; }
.per-head { font-size:.7rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted);
            font-weight:700; margin:.7rem 0 .25rem; }
.per-head:first-child { margin-top:0; }
.per-i a { display:flex; flex-direction:column; gap:.05rem; padding:.4rem .5rem; border-radius:7px;
           text-decoration:none; color:inherit; }
.per-i a:hover { background:var(--tint-6); }
.per-i.is-sel a { background:var(--tint-6); border-left:3px solid var(--accent); padding-left:calc(.5rem - 3px); }
.per-name { font-weight:600; font-size:.9rem; }
.per-role { font-size:.76rem; }
.per-noacct { font-size:.68rem; font-style:italic; }
.per-when { display:flex; gap:.5rem; align-items:flex-end; flex-wrap:wrap; }
.per-when label { display:flex; flex-direction:column; font-size:.76rem; color:var(--muted); gap:.15rem; }
.per-h { margin:1.4rem 0 .5rem; font-size:1rem; }
.per-h:first-of-type { margin-top:1rem; }

.per-acts { list-style:none; margin:0; padding:0; }
.per-act { display:grid; grid-template-columns:auto 1fr auto; gap:.6rem; align-items:baseline;
           padding:.4rem .2rem; border-top:1px solid var(--border); font-size:.86rem; }
.per-act:first-child { border-top:none; }
.per-act-k { font-size:.68rem; text-transform:uppercase; letter-spacing:.05em; color:var(--muted);
             font-weight:700; min-width:3.4em; }
.per-act.is-open .per-act-k { color:var(--warn-fg); }
.per-act-b p { margin:0; }
.per-act-m { font-size:.76rem; white-space:nowrap; }
@media (max-width: 700px) { .per-act { grid-template-columns:1fr; } .per-act-m { white-space:normal; } }

/* The switches. Links, not checkboxes, so every view has a URL to send. */
.per-switch { display:flex; flex-direction:column; gap:.45rem; margin:.8rem 0 1rem; }
.per-switch-row { display:flex; gap:.35rem; flex-wrap:wrap; align-items:center; }
.per-switch-l { font-size:.7rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted);
                font-weight:700; min-width:3em; }
.per-tog { font-size:.78rem; padding:.18em .6em; border-radius:99px; text-decoration:none;
           border:1px solid var(--border); color:var(--muted); background:transparent; }
.per-tog:hover { color:var(--text); text-decoration:none; }
.per-tog.is-on { color:var(--text); border-color:var(--accent); background:var(--tint-6); }
.per-tog.is-locked { cursor:default; opacity:.75; }
/* A person's chip carries their own colour, so the switch and the marks it
   turns on are recognisably the same thing. */
.per-tog.per-who.is-on { border-color: hsl(var(--pc-h, 210) 55% 52%);
                         background: hsl(var(--pc-h, 210) 55% 52% / .16); }

/* The month. Built on the calendar's grid so the two pages line up. */
.per-cal .cal-cell { min-height: 86px; }
.per-ev { display:block; font-size:.68rem; line-height:1.35; padding:.05rem .32rem; margin-bottom:2px;
          border-radius:4px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
          text-decoration:none; color:#fff; background: hsl(var(--pc-h, 210) 42% 38%);
          border-left:3px solid transparent; }
:root[data-theme="light"] .per-ev { background: hsl(var(--pc-h, 210) 46% 42%); }
a.per-ev:hover { text-decoration:none; opacity:.9; }
/* The kind is the left edge; the person is the fill. Two facts, two places. */
.per-ev.per-k-work     { border-left-color:#46c97b; }
.per-ev.per-k-quotes   { border-left-color:#5b96e8; }
.per-ev.per-k-activity { border-left-color:#e8b45a; }
.per-ev.per-k-tasks    { border-left-style:dotted; border-left-color:#e0696d; }
.per-ev.is-done { opacity:.5; text-decoration:line-through; }
.per-ev.per-rest { background:transparent; color:var(--muted); border-left-color:transparent;
                   cursor:help; font-style:italic; }
.per-more { font-size:.66rem; }
.per-key { font-size:.78rem; margin:.7rem 0 0; }

/* ===================== The warehouse board ================================
   Two audiences, one stylesheet. The board hangs on a wall and is read from
   across the room, so its type is large and its lanes are columns. The per-show
   page is held in one hand over a road case, so its controls are thumb-sized
   and its tables scroll rather than shrink. */

.wh-lanes { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:1rem; align-items:start; }
@media (max-width: 900px) { .wh-lanes { grid-template-columns:1fr; } }

/* A wall screen is wider than a document. The page column is sized for reading
   and this is not reading, so on anything big the board takes the width it is
   given. :has() is the whole of the trick, and a browser without it simply gets
   the normal column -- narrower, still correct, still legible. */
@media (min-width: 1200px) {
  .wrap:has(#whboard) { max-width: 2200px; }
}
.wh-lane { border:1px solid var(--line); border-radius:12px; padding:.9rem; min-width:0; }
/* The lane a warehouse has to clear first is the one it can see first. */
.wh-lane.wh-back { border-left:4px solid var(--bad-fg); }
.wh-lane.wh-pull { border-left:4px solid var(--accent); }
.wh-lane.wh-out  { border-left:4px solid var(--line); }
.wh-lane h2 { margin:0; font-size:1.15rem; display:flex; align-items:center; gap:.5rem; }
.wh-lane .wh-n { font-size:.85rem; font-weight:600; background:var(--tint-6); border-radius:999px;
                 padding:.05em .6em; min-width:1.8em; text-align:center; }
.wh-blurb { margin:.15rem 0 .8rem; font-size:.82rem; color:var(--muted); }
.wh-empty { color:var(--muted); font-size:.9rem; margin:0; padding:.6rem 0; }

/* One show. A link the size of a card, because the whole thing is the target
   for somebody tapping it with gloves on. */
.wh-card { display:block; text-decoration:none; color:inherit; border:1px solid var(--line);
           border-radius:10px; padding:.75rem .85rem; margin-bottom:.6rem; background:var(--tint-6); }
.wh-card:hover { border-color:var(--accent); }
.wh-card.is-late { border-color:var(--bad-fg); }
.wh-card-top { display:flex; gap:.6rem; align-items:baseline; justify-content:space-between; }
.wh-name { font-size:1.15rem; font-weight:700; line-height:1.2; min-width:0; overflow-wrap:anywhere; }
.wh-due { font-size:.85rem; color:var(--muted); white-space:nowrap; }
.wh-due.is-late { color:var(--bad-fg); font-weight:700; }
.wh-sub { display:flex; flex-wrap:wrap; gap:.15rem .7rem; font-size:.82rem; color:var(--muted); margin:.15rem 0 .5rem; }

.wh-bar { height:8px; border-radius:999px; background:var(--line); overflow:hidden; }
.wh-bar > span { display:block; height:100%; background:var(--accent); border-radius:999px; }
.wh-bar-lg { height:12px; }
.wh-back .wh-bar > span { background:var(--bad-fg); }

.wh-counts { margin-top:.45rem; font-size:.9rem; display:flex; flex-wrap:wrap; gap:.4rem; align-items:baseline; }
.wh-counts strong { font-size:1.3rem; font-variant-numeric:tabular-nums; }
.wh-of { color:var(--muted); }
.wh-chip { font-size:.75rem; background:var(--tint-6); border:1px solid var(--line);
           border-radius:999px; padding:.05em .55em; color:var(--muted); }
.wh-chip.wh-good { color:var(--good-fg, inherit); }

.wh-foot { font-size:.8rem; margin-top:.8rem; }
.wh-foot.is-stale { color:var(--bad-fg); font-weight:600; }

/* On a wall the page furniture is noise, so the board takes the whole screen
   and everything grows with it. */
:fullscreen .page-head, :fullscreen .wh-foot { display:none; }
:fullscreen .wh-lane h2 { font-size:1.6rem; }
:fullscreen .wh-name { font-size:1.7rem; }
:fullscreen .wh-counts strong { font-size:2rem; }
:fullscreen .wh-sub, :fullscreen .wh-due { font-size:1rem; }

/* ---- the per-show page ---- */
.wh-docs { display:flex; gap:.5rem; flex-wrap:wrap; align-items:flex-start; }
.wh-dir { display:flex; gap:.4rem; margin-bottom:1rem; }
.wh-dir-b { flex:1; text-align:center; text-decoration:none; color:inherit; font-weight:600;
            border:1px solid var(--line); border-radius:10px; padding:.7rem .9rem; }
.wh-dir-b.is-on { border-color:var(--accent); background:var(--tint-6); }
.wh-progress { display:flex; flex-direction:column; gap:.45rem; }
.wh-progress-n { display:flex; flex-wrap:wrap; gap:.5rem; align-items:baseline; }
.wh-progress-n strong { font-size:1.6rem; font-variant-numeric:tabular-nums; }
/* On a phone the tag box was sharing a row with two buttons and ended up too
   narrow to read what had been typed into it. The box gets the row; the
   buttons get the one under it, at a size a gloved thumb can hit. */
@media (max-width: 560px) {
  .wh-scan { display:grid; grid-template-columns:1fr 1fr; gap:.5rem; }
  .wh-scan input[name="tag"] { grid-column:1 / -1; width:100%; font-size:1.05rem; }
  .wh-scan .btn { padding-top:.75rem; padding-bottom:.75rem; }
}
.wh-lines td { vertical-align:middle; }
/* Done, but still legible -- a struck-through list is unreadable at arm's
   length and this one gets read at arm's length. */
.wh-lines tr.wh-done td { opacity:.55; }

/* Permanent, and it should not look like the reversible action beside it. */
.crew-purge { color: var(--bad-fg); }
.crew-purge:hover { color: var(--bad-fg2, var(--bad-fg)); }

/* Booking warnings -- the same two levels wherever they are shown.
   A block is a fact the booking contradicts, a warn is a judgement call, and
   the chip carries the whole list in its title so a roster row can say there
   is a problem without growing a paragraph. */
/* Sub-folders, folded under the folder they belong to. Closed on every load:
   the standard tree is sixteen folders and a show that opens on all of them
   shows nothing in particular. The summary says what is behind it. */
.doc-kids { margin:.15rem 0 .5rem; }
.doc-kids > summary { cursor:pointer; font-size:.82rem; color:var(--muted);
                      padding:.2rem 0; list-style:none; display:inline-flex;
                      gap:.35rem; align-items:center; }
.doc-kids > summary::-webkit-details-marker { display:none; }
.doc-kids > summary::before { content:'\25B8'; font-size:.7rem; transition:transform .12s ease; }
.doc-kids[open] > summary::before { transform:rotate(90deg); }
.doc-kids > summary:hover { color:var(--text); }
.doc-kids > .doc-group { margin-left:.9rem; }

.bk-chip { display: inline-block; font-size: .68rem; font-weight: 700; line-height: 1.5;
           padding: .05em .5em; border-radius: 99px; border: 1px solid transparent;
           white-space: nowrap; cursor: help; }
.bk-chip.bk-block { background: var(--bad-bg); color: var(--bad-fg); border-color: var(--bad-bd); }
.bk-chip.bk-warn  { background: var(--warn-bg); color: var(--warn-fg); border-color: var(--warn-bd); }

.bk-notice { border: 1px solid var(--warn-line); border-left-width: 3px; border-radius: 8px;
             padding: .5rem .7rem; margin: .6rem 0; font-size: .85rem; background: var(--tint-6); }
.bk-notice.bk-block { border-color: var(--bad-line); }
.bk-notice p { margin: 0; }
.bk-notice ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.bk-notice li { margin: .15rem 0; }
.bk-notice li.bk-block { color: var(--bad-fg); }
.bk-notice li.bk-warn  { color: var(--warn-fg); }
/* A note is about the person, not about these dates -- it is shown when
   somebody is looking at them and never raises a chip. It reads as ordinary
   text on purpose. */
.bk-notice.bk-note { border-color: var(--border); border-left-color: var(--muted); }
.bk-notice li.bk-note { color: var(--muted); }

.fill-dl { display: grid; grid-template-columns: auto 1fr; gap: .35rem .8rem;
           margin: .8rem 0; font-size: .85rem; align-items: baseline; }
.fill-dl dt { color: var(--muted); white-space: nowrap; }
.fill-dl dd { margin: 0; }

.fill-msg { margin: 0 0 .8rem; padding: .5rem .8rem; border-radius: 8px; font-size: .87rem;
            border: 1px solid var(--border); background: var(--panel-2); }
.fill-msg.ok  { border-color: var(--ok-line); }
.fill-msg.err { border-color: var(--bad-line); }
.fill-assign-inline { margin-left: .4rem; }

/* Travel table. Its columns carry short facts that must not break mid-token:
   an order number split over two lines ("#100 / 2") reads as a different
   number, and "Groun / d" reads as nothing at all. Holding those columns
   whole is enough on its own -- a min-width as well pushed edit and delete
   off the right edge, hiding the controls behind a scrollbar. The prose
   columns give up the width instead, which is what they are for. */
.tv-table .tv-nb { white-space: nowrap; }
.tv-table td:nth-child(7) { min-width: 8rem; }   /* venue wraps, not sprawls */

.ts-filters { display:flex; gap:.9rem; align-items:flex-end; flex-wrap:wrap; margin:.4rem 0 .9rem; }
.ts-filters label { display:flex; flex-direction:column; gap:.2rem; font-size:.78rem; }
.ts-table th a { text-decoration:none; }
.ts-table th a:hover { text-decoration:underline; }
.ts-note { max-width:170px; }
/* The note opens in place. A pasted paragraph used to set the height of the
   whole row it sat in. */
.ts-note > details > summary { cursor:pointer; font-size:.8rem; color:var(--muted); }
.ts-note-body { margin-top:.3rem; font-size:.82rem; white-space:pre-wrap;
  max-height:9rem; overflow-y:auto; }
.ts-actions { white-space:nowrap; }
.ts-edit { display:inline-block; }
.ts-edit > summary { display:inline; }
.ts-editform { margin:.5rem 0 .3rem; min-width:340px; }

/* The conversation attached to an agreement. The client's side is marked so
   staff can tell at a glance who asked for what. */
.note-thread { list-style:none; margin:.6rem 0 0; padding:0; display:flex; flex-direction:column; gap:.55rem; }
.note-thread .note { border:1px solid var(--border); border-radius:6px; padding:.5rem .7rem; }
.note-thread .note-client { border-left:3px solid var(--note-line); }
.note-who { font-size:.8rem; font-weight:600; display:flex; gap:.5rem; align-items:baseline; }
.note-body { font-size:.88rem; margin-top:.2rem; white-space:pre-wrap; }

/* A margin reads as one of three things, so it is coloured as one of three
   things. .loss already exists for red. */
.good { color: var(--green, #3fb27f); }
.warn { color: var(--rdy-amber, #d98a10); }
.wr-summary { max-width:560px; }
.wr-gear input, .wr-pos input { font-size:.82rem; }
.wr-gear td, .wr-pos td { vertical-align:top; }
.wr-short { color: var(--red); font-weight:600; font-size:.82rem; }
.wr-ok { color: var(--muted); font-size:.82rem; }

/* Travel: a switch on the row, the detail underneath where there is room for
   eight expenses and their costs without widening an already wide grid. */
.lgrid-travel { white-space:nowrap; }
.tv-toggle { display:flex; align-items:center; gap:.3rem; font-size:.8rem; }
.tv-panels { margin:1.2rem 0 1rem; padding-top:.9rem; border-top:1px solid var(--border); }
.tv-panels h3 { margin:0 0 .2rem; font-size:1rem; }
.tv-pos { border:1px solid var(--border); border-radius:6px; margin:.5rem 0; }
.tv-pos > summary { cursor:pointer; padding:.45rem .7rem; font-weight:600; font-size:.88rem;
  display:flex; gap:.5rem; align-items:baseline; }
.tv-items { display:grid; grid-template-columns:repeat(auto-fit, minmax(215px, 1fr));
  gap:.5rem .9rem; padding:.2rem .7rem .8rem; }
.tv-item { display:flex; align-items:center; gap:.45rem; min-width:0; }
.tv-item .tv-name { flex:1; min-width:0; font-size:.82rem; display:flex; flex-direction:column; }
.tv-item .tv-name small { font-size:.7rem; }
.tv-item input[type=number] { width:84px; }
.tv-total { margin:.7rem 0 0; font-size:.9rem; }

.show-defaults { display:block; }
.show-defaults .card { margin:0 0 1rem; }
.show-defaults .card:last-child { margin-bottom:.4rem; }

.rule-actions { display:flex; gap:.6rem; align-items:center; flex-wrap:wrap; margin-top:.2rem; }
.rule-actions + .rule-actions { margin-top:.7rem; }

.agenda-addroom-top { margin:0 0 1rem; padding:0 0 .9rem; border-top:none; border-bottom:1px solid var(--border); }
.sk-badge { background:var(--panel-2); color:var(--muted); border-color:var(--border); }
.agenda-session.sk-load_in, .agenda-session.sk-load_out { border-left-color:var(--warn-line); }
.agenda-session.sk-general { border-left-color:var(--ok-line); }
.agenda-session.sk-rehearsal { border-left-color:var(--note-line); }
.agenda-session.sk-meal, .agenda-session.sk-reception { border-left-color:var(--info-line); }
.agenda-session.sk-break { border-left-color:var(--cool-line); }
.agenda-session.sk-call_time, .agenda-session.sk-eod { border-left-color:var(--pink-line); }
.agenda-session.sk-dark { border-left-color:var(--dim-line); opacity:.7; }
/* Chosen rather than inherited from a borrowed kind. Named colours, not hex:
   an agenda is read on both themes and each has its own value for these. */
.agenda-session.sk-c-ok,   .agenda-preset.sk-c-ok   { border-left-color:var(--ok-line); }
.agenda-session.sk-c-warn, .agenda-preset.sk-c-warn { border-left-color:var(--warn-line); }
.agenda-session.sk-c-info, .agenda-preset.sk-c-info { border-left-color:var(--info-line); }
.agenda-session.sk-c-note, .agenda-preset.sk-c-note { border-left-color:var(--note-line); }
.agenda-session.sk-c-cool, .agenda-preset.sk-c-cool { border-left-color:var(--cool-line); }
.agenda-session.sk-c-pink, .agenda-preset.sk-c-pink { border-left-color:var(--pink-line); }
.agenda-session.sk-c-bad,  .agenda-preset.sk-c-bad  { border-left-color:var(--bad-line); }
.agenda-session.sk-c-dim,  .agenda-preset.sk-c-dim  { border-left-color:var(--dim-line); }
.agenda-session.sk-c-lime, .agenda-preset.sk-c-lime { border-left-color:var(--lime-line); }
/* The colour a block is set to, shown beside the picker -- reading "Teal" and
   trusting it is what let a colour nobody could distinguish sit there. */
.blk-swatch { display:inline-block; width:.85rem; height:.85rem; border-radius:3px;
  vertical-align:-2px; margin-right:.4rem; border:1px solid var(--border); }
.blk-swatch.sw-none { background:var(--accent-2); }
.blk-swatch.sw-ok   { background:var(--ok-line); }
.blk-swatch.sw-warn { background:var(--warn-line); }
.blk-swatch.sw-info { background:var(--info-line); }
.blk-swatch.sw-note { background:var(--note-line); }
.blk-swatch.sw-cool { background:var(--cool-line); }
.blk-swatch.sw-pink { background:var(--pink-line); }
.blk-swatch.sw-bad  { background:var(--bad-line); }
.blk-swatch.sw-dim  { background:var(--dim-line); }
.blk-swatch.sw-lime { background:var(--lime-line); }

/* Drag & drop: a palette of blocks above the grid, room columns as drop zones */
.agenda-palette { display:flex; flex-wrap:wrap; align-items:center; gap:.4rem; margin:0 0 .8rem; }
.agenda-palette-l { font-size:.75rem; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin-right:.15rem; }
.agenda-palette-h { flex-basis:100%; font-size:.76rem; }
.agenda-preset { display:inline-block; cursor:grab; user-select:none; font-size:.78rem; font-weight:600;
  padding:.28rem .55rem; border:1px solid var(--border); border-left:3px solid var(--accent-2);
  border-radius:7px; background:var(--panel-2); color:var(--text); }
.agenda-preset:hover { border-color:var(--accent-2); }
.agenda-preset.sk-load_in, .agenda-preset.sk-load_out { border-left-color:var(--warn-line); }
.agenda-preset.sk-general { border-left-color:var(--ok-line); }
.agenda-preset.sk-rehearsal { border-left-color:var(--note-line); }
.agenda-preset.sk-meal, .agenda-preset.sk-reception { border-left-color:var(--info-line); }
.agenda-preset.sk-break { border-left-color:var(--cool-line); }
.agenda-preset.sk-call_time, .agenda-preset.sk-eod { border-left-color:var(--pink-line); }
.agenda-session[draggable=true] { cursor:grab; }
.dragging { opacity:.45; }
.agenda-room.drop-on, .agenda-unassigned.drop-on { outline:2px dashed var(--accent-2); outline-offset:2px; }
/* The line showing where the block will land, between the two it goes between.
   The dot on the left gives it a start, so a line at the very top of a column
   does not read as the column's own border. */
.agenda-dropline {
  position:relative; height:0; margin:.25rem 0;
  border-top:2px solid var(--accent); border-radius:2px;
  box-shadow:0 0 6px color-mix(in srgb, var(--accent) 60%, transparent);
}
.agenda-dropline::before {
  content:''; position:absolute; left:-3px; top:-4px;
  width:7px; height:7px; border-radius:50%; background:var(--accent);
}
.agenda-day.drop-on { outline:2px dashed var(--accent-2); outline-offset:2px; }
.agenda-unassigned.is-empty { opacity:.75; }
.agenda-msg { margin:0 0 .8rem; padding:.45rem .7rem; border-radius:8px; font-size:.85rem;
  border:1px solid var(--border); background:var(--panel-2); }
.agenda-msg.ok  { border-color:#3d7d52; }
.agenda-msg.err { border-color:#8d4141; }

/* Bulk actions on a list */
.bulkbar { display:flex; gap:1rem; align-items:center; flex-wrap:wrap; margin-bottom:.8rem;
  padding:.6rem .8rem; border:1px solid var(--border); border-radius:9px; background:var(--panel-2);
  font-size:.85rem; }

/* Blank import templates */
.tpl-grid { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .6rem; }

/* Sortable table headings */
.table th.sortable { padding: 0; }
.table th.sortable > a { display: block; padding: .6rem .7rem; color: inherit; white-space: nowrap; }
.table th.sortable > a:hover { color: var(--text); text-decoration: none; background: rgba(120,140,170,.08); }
.table th.num.sortable > a { text-align: right; }
.table th.sorted > a { color: var(--text); }
.sort-arrow { margin-left: .3rem; color: var(--accent); }
/* Every one of these headings sorts, but only the active one said so, which
   read as "this table sorts by name" rather than "pick a column". The rest now
   carry a faint pair of arrows that firms up under the pointer. */
.table th.sortable:not(.sorted) > a::after {
  content: '\2195'; margin-left: .3rem; opacity: .28; font-size: .9em;
}
.table th.sortable:not(.sorted) > a:hover::after { opacity: .7; }

/* Save and Cancel, side by side */
.form-actions { display: flex; gap: .6rem; align-items: center; }
.btn-cancel { background: transparent; }

/* Hours inbox */
.inbox-bulk { display:flex; gap:1rem; align-items:center; flex-wrap:wrap; font-size:.88rem; }
.inbox-bulk span { flex:1 1 20rem; }

/* API key scopes — one tick box per line, so the descriptions stay readable */
.scope-grid { display:grid; gap:.3rem; margin-top:.4rem; }
.scope-grid .taxlabel { align-items:flex-start; }
.scope-grid code { font-size:.8rem; }

/* Picking how dates read, on the Branding page */
.date-form { display:grid; gap:.5rem; max-width:34rem; }
.date-opt { display:flex; gap:.65rem; align-items:flex-start; padding:.6rem .75rem;
  border:1px solid var(--border); border-radius:9px; background:var(--panel-2); cursor:pointer; }
.date-opt:hover { border-color:var(--accent-2); }
.date-opt.is-on { border-color:var(--accent-2); background:color-mix(in srgb, var(--accent-2) 10%, var(--panel-2)); }
.date-opt input { margin-top:.25rem; }
.date-opt-b { display:flex; flex-direction:column; gap:.1rem; min-width:0; }
.date-opt-b strong { font-size:.95rem; font-variant-numeric:tabular-nums; }
.date-opt-b .muted { font-size:.78rem; font-variant-numeric:tabular-nums; }
.date-form .btn { justify-self:start; }

/* Signed documents on a crew profile */
.cp-docs { margin:.4rem 0 .6rem; }
.cp-doc { padding:.55rem 0; border-top:1px solid var(--border); font-size:.9rem; }
.cp-doc:first-child { border-top:none; }
.cp-doc .copyfield { margin-top:.35rem; font-size:.72rem; }
.cp-newdoc { display:flex; gap:.4rem; align-items:center; flex-wrap:wrap; }
.cp-newdoc select { max-width:14rem; }

/* Insurance certificates on a crew profile */
.cp-policy { padding:.6rem 0; border-top:1px solid var(--border); font-size:.9rem; }
.cp-policy:first-of-type { border-top:none; }
.cp-policy-doc { display:flex; gap:.7rem; align-items:center; font-size:.82rem; margin-top:.3rem; }
.cp-policy-up { display:flex; gap:.4rem; align-items:center; flex-wrap:wrap; margin-top:.4rem; }
.cp-policy-up input[type=file] { font-size:.78rem; padding:.3em; }

/* A preferred name, next to the legal one */
.nickname { color:var(--muted); font-weight:500; }

/* ============================ Event number & coverage ============================ */
.event-no { font-family:ui-monospace,Consolas,monospace; color:var(--muted); font-size:.82em; font-weight:600; }
h1 .event-no { font-size:.6em; vertical-align:.2em; }
/* Assign-a-block form */
.cov-assign-row { display:flex; gap:.8rem; flex-wrap:wrap; align-items:flex-end; margin-bottom:.9rem; }
.cov-assign-row label { display:flex; flex-direction:column; gap:.2rem; font-size:.8rem; color:var(--muted); }
.cov-assign-row select, .cov-assign-row input { padding:.5em .6em; border:1px solid var(--border); border-radius:8px; background:var(--panel-2); color:var(--text); font:inherit; }
.cov-pickers { display:grid; grid-template-columns:repeat(auto-fit,minmax(220px,1fr)); gap:1rem; margin-bottom:.9rem; }
.cov-picker { border:1px solid var(--border); border-radius:10px; padding:.6rem .7rem; }
.cov-picker-h { display:flex; justify-content:space-between; font-size:.75rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:.4rem; }
.cov-check { display:block; font-size:.85rem; padding:.15rem 0; }
/* Grid */
.cov-grid th, .cov-grid td { vertical-align:top; }
.cov-room { white-space:nowrap; position:sticky; left:0; background:var(--panel); }
.cov-cell { min-width:110px; padding:.35rem !important; }
.cov-cell.cov-on { background:rgba(63,185,107,.07); }
.cov-cell.cov-gap { background:rgba(224,93,93,.10); }
.cov-person { display:flex; align-items:center; gap:.3rem; font-size:.78rem; background:var(--panel-2); border:1px solid var(--border); border-radius:6px; padding:.2rem .35rem; margin-bottom:.25rem; }
.cov-name { font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cov-time { color:var(--muted); font-size:.7rem; font-variant-numeric:tabular-nums; white-space:nowrap; }
.cov-x { margin-left:auto; background:none; border:none; color:var(--muted); cursor:pointer; padding:0 .1rem; font-size:.75rem; }
.cov-x:hover { color:var(--red); }
.cov-gap-l { font-size:.7rem; color:var(--bad-fg); }

/* ============================ Branding controls ============================ */
.brand-style-row { display:flex; gap:1.4rem; align-items:center; padding:.3rem 0; }
.brand-prev { border-radius:10px; overflow:hidden; border:1px solid var(--border); }
.brand-prev-bar { padding:16px 18px; display:flex; align-items:center; gap:12px; min-height:72px; }
.brand-prev-bar img { height:40px; display:block; max-width:60%; object-fit:contain; }
.brand-prev-bar span { font-weight:700; font-size:1.1rem; }
.brand-prev-body { background:#fff; color:#1c2430; padding:14px 18px; }
.brand-prev-h { font-weight:700; font-size:.95rem; border-bottom:2px solid; padding-bottom:.3rem; }
.brand-swatches { display:flex; gap:1rem; flex-wrap:wrap; margin-top:.8rem; font-size:.78rem; }
.brand-swatches > div { display:flex; align-items:center; gap:.4rem; }
.brand-chip { width:18px; height:18px; border-radius:5px; border:1px solid var(--border); display:inline-block; }
.brand-sub { margin:1.2rem 0 0; font-size:.95rem; border-top:1px solid var(--border); padding-top:1rem; }
.brand-hint { margin:.15rem 0 .2rem; font-size:.82rem; }
/* Miniature of the show-book cover — same recipe as cover_css(), 8.5:11 */
.cover-prev { border-radius:10px; border:1px solid var(--border); aspect-ratio:8.5/11;
              padding:9% 8% 8%; display:flex; flex-direction:column; overflow:hidden; }
.cover-prev img { height:26px; width:auto; max-width:72%; object-fit:contain; display:block; }
.cover-prev-fill { flex:1; min-height:14px; }
.cover-prev-kick { font-size:.55rem; letter-spacing:.24em; text-transform:uppercase; font-weight:700; }
.cover-prev-title { font-size:1.35rem; font-weight:800; line-height:1.05; margin:.3em 0 .1em; }
.cover-prev-rule { width:36px; height:3px; border-radius:2px; margin:.6rem 0 .5rem; }
.cover-prev-sub { font-size:.68rem; }

/* ============================ Show readiness ============================ */
:root { --rdy-green:#1f9d55; --rdy-amber:#d98a10; --rdy-red:#d64545; --rdy-grey:#8a94a3; }
:root[data-theme="light"] { --rdy-green:#15804a; --rdy-amber:#a8690b; --rdy-red:#c0342f; --rdy-grey:#5d6675; }
.rdy-card { border-top:3px solid transparent; }
.rdy-head { display:flex; align-items:center; gap:1.1rem; margin-bottom:1rem; }
.rdy-title h2 { margin:0; }
.rdy-title p { margin:.15rem 0 0; }
/* Conic dial: filled arc = score, track = the rest. Colour from band. */
.rdy-dial { --c:var(--rdy-grey); position:relative; flex:none; width:74px; height:74px; border-radius:50%;
  background:conic-gradient(var(--c) var(--deg), var(--panel-2,#e6eaf1) 0);
  display:grid; place-items:center; }
.rdy-dial::before { content:""; position:absolute; width:56px; height:56px; border-radius:50%; background:var(--panel,#fff); }
.rdy-num { position:relative; font-weight:800; font-size:1.15rem; color:var(--c); font-variant-numeric:tabular-nums; }
.rdy-num small { color:inherit; font-size:.6em; font-weight:700; }   /* same reason as .rdy-chip small */
.rdy-green { --c:var(--rdy-green); } .rdy-amber { --c:var(--rdy-amber); }
.rdy-red   { --c:var(--rdy-red);   } .rdy-grey  { --c:var(--rdy-grey);  }
/* Two columns, divided: winning the work on the left, delivering it on the
   right. The rule between them is the point of the split, so it is a real
   border rather than a gap — and it becomes a horizontal one when the columns
   stack, so the two halves stay legible on a phone. */
/* The rule lives on the second column, not in a track of its own: a
   pseudo-element placed at column 2 competes with auto-placement and the two
   halves end up stacked in the wrong cells. */
.rdy-cols { display:grid; grid-template-columns:1fr 1fr; gap:0 1.6rem; margin-top:.4rem; align-items:start; }
.rdy-col { min-width:0; }
.rdy-col + .rdy-col { border-left:1px solid var(--border); padding-left:1.6rem; }
.rdy-colhead { padding-bottom:.5rem; margin-bottom:.4rem; border-bottom:1px solid var(--border);
  display:grid; grid-template-columns:1fr auto; align-items:baseline; gap:.5rem; }
.rdy-colhead h3 { margin:0; font-size:.95rem; }
.rdy-colhead p { grid-column:1 / -1; margin:.1rem 0 0; font-size:.76rem; }
.rdy-colcount { font-size:.8rem; font-weight:700; color:var(--muted); font-variant-numeric:tabular-nums; }
@media (max-width:760px) {
  .rdy-cols { grid-template-columns:1fr; gap:1rem; }
  .rdy-col + .rdy-col { border-left:none; padding-left:0;
    border-top:1px solid var(--border); padding-top:1rem; }
}
.rdy-list { list-style:none; margin:0; padding:0; display:grid; gap:.2rem; }
.rdy-i a { display:flex; align-items:center; gap:.55rem; padding:.4rem .5rem; border-radius:7px;
  text-decoration:none; color:inherit; }
.rdy-i a:hover { background:rgba(120,140,170,.1); }
.rdy-ic { flex:none; width:20px; height:20px; border-radius:50%; display:grid; place-items:center;
  font-size:.72rem; font-weight:700; color:#fff; background:var(--rdy-grey); }
.rdy-ok  .rdy-ic { background:var(--rdy-green); }
.rdy-part .rdy-ic { background:var(--rdy-amber); }
.rdy-todo .rdy-ic { background:#c3cbd6; color:#5a6675; }
.rdy-l { font-weight:600; font-size:.9rem; }
.rdy-d { margin-left:auto; font-size:.8rem; color:var(--muted,#64748b); text-align:right; }
.rdy-todo .rdy-d, .rdy-part .rdy-d { color:var(--rdy-red); }
.rdy-part .rdy-d { color:var(--rdy-amber); }
/* Inline chip for list rows */
.rdy-chip { display:inline-block; min-width:2.8em; padding:.1em .5em; border-radius:20px;
  font-weight:700; font-size:.82rem; font-variant-numeric:tabular-nums; color:#fff;
  white-space:nowrap; }
/* `small, .muted { color: var(--muted) }` was colouring the per-cent sign grey,
   and grey on a coloured pill reads as nothing there — hence "84" with no unit.
   It takes the chip's own colour, like the number it belongs to. */
.rdy-chip small { color:inherit; font-size:.78em; font-weight:700; opacity:.9; margin-left:.5px; }
.rdy-chip.rdy-green { background:var(--rdy-green); } .rdy-chip.rdy-amber { background:var(--rdy-amber); }
.rdy-chip.rdy-red   { background:var(--rdy-red);   } .rdy-chip.rdy-grey  { background:var(--rdy-grey); }

/* ============================ Show P&L ============================ */
.pnl-head { display:flex; align-items:baseline; gap:1rem; flex-wrap:wrap; margin-bottom:1rem; }
.pnl-head h2 { margin:0; }
.pnl-flag { margin-left:auto; font-weight:800; font-size:1rem; padding:.2em .7em; border-radius:20px;
  font-variant-numeric:tabular-nums; color:#fff; }
.pnl-flag.pnl-green { background:var(--rdy-green); } .pnl-flag.pnl-amber { background:var(--rdy-amber); }
.pnl-flag.pnl-red { background:var(--rdy-red); } .pnl-flag.pnl-grey { background:var(--rdy-grey); }
.pnl-cols { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
@media(max-width:560px){ .pnl-cols { grid-template-columns:1fr; } }
.pnl-col { background:var(--panel-2); border:1px solid var(--border); border-radius:10px; padding:14px 16px; }
.pnl-track { border-left:3px solid var(--rdy-grey); }
.pnl-track.pnl-green { border-left-color:var(--rdy-green); }
.pnl-track.pnl-amber { border-left-color:var(--rdy-amber); }
.pnl-track.pnl-red { border-left-color:var(--rdy-red); }
.pnl-k { font-size:.72rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); font-weight:700; margin-bottom:.35rem; }
.pnl-badge { background:rgba(31,157,85,.18); color:#5fd39a; border-radius:20px; padding:0 .5em; font-size:.62rem; letter-spacing:.04em; }
.pnl-rev { font-size:1.5rem; font-weight:800; font-variant-numeric:tabular-nums; }
.pnl-sub { font-size:.8rem; color:var(--muted); margin-top:.1rem; }
.pnl-m { margin-top:.5rem; font-size:1.05rem; font-weight:700; font-variant-numeric:tabular-nums; }
.pnl-mp { font-size:.85rem; color:var(--muted); font-weight:600; }
.pnl-read { margin:.9rem 0 0; font-weight:600; font-size:.92rem; }
.pnl-read.pnl-good { color:#4fbf85; } .pnl-read.pnl-bad { color:#e0817f; }
.pnl-costs { display:flex; gap:.6rem; flex-wrap:wrap; margin-top:1rem; }
.pnl-cost { background:var(--panel-2); border:1px solid var(--border); border-radius:8px; padding:.5rem .8rem; font-size:.82rem; }
.pnl-cost span { color:var(--muted); margin-right:.4rem; }
.pnl-cost b { font-variant-numeric:tabular-nums; }
.pnl-note { margin:.9rem 0 0; font-size:.82rem; color:var(--muted); }

/* ============================ Staffing snapshot ============================ */
/* The floor row on the P&L card: what the show is judged against, whose
   number it is, and -- when it is under -- what that is worth in money. */
.pnl-floor { display:flex; align-items:baseline; gap:.8rem; flex-wrap:wrap; margin-top:1rem;
             padding-top:.7rem; border-top:1px solid var(--border); font-size:.85rem; }
.pnl-floor b { font-variant-numeric:tabular-nums; }
.pnl-floor.is-under b { color:var(--rdy-red); }
.pnl-short { font-weight:700; color:var(--rdy-red); font-variant-numeric:tabular-nums; }
.pnl-setfloor { margin-left:auto; }
.pnl-setfloor summary { font-size:.82rem; }
.pnl-setfloor form { margin-top:.5rem; display:flex; gap:.4rem; align-items:center; flex-wrap:wrap; }

/* Margin watch -- the same shape as the staffing card next to it, because it
   answers the same kind of question and they are read one after the other. */
.mw-card { border-left:3px solid var(--rdy-red); }
.mw-clear { border-left-color:var(--rdy-green); }
.mw-head { display:flex; align-items:baseline; gap:1rem; flex-wrap:wrap; margin-bottom:.7rem; }
.mw-head h2 { margin:0; }
.mw-tots { display:flex; gap:.9rem; flex-wrap:wrap; align-items:baseline; margin-left:auto; font-size:.85rem; }
.mw-tot b { font-size:1.05rem; font-variant-numeric:tabular-nums; }
.mw-tot.mw-red b { color:var(--rdy-red); }
.mw-list { list-style:none; margin:0; padding:0; }
.mw-i + .mw-i { border-top:1px solid var(--border); }
.mw-i a { display:flex; align-items:center; gap:.8rem; padding:.55rem .3rem; text-decoration:none; color:inherit; }
.mw-i a:hover { background:rgba(120,140,170,.08); border-radius:6px; }
.mw-when { flex:none; width:6.2em; font-size:.82rem; font-weight:700; text-transform:uppercase;
           letter-spacing:.03em; color:var(--muted); }
.mw-name { font-weight:600; }
.mw-name .evno { font-family:ui-monospace,Consolas,monospace; font-size:.78rem; color:var(--muted); }
.mw-nums { margin-left:auto; display:flex; gap:.7rem; align-items:baseline; flex-wrap:wrap;
           justify-content:flex-end; font-variant-numeric:tabular-nums; }
.mw-pct { font-weight:800; }
.mw-red .mw-pct { color:var(--rdy-red); }
.mw-amber .mw-pct { color:var(--rdy-amber); }
.mw-floor, .mw-gap { font-size:.8rem; }
.mw-gap { font-weight:700; color:var(--rdy-red); }
.mw-more { margin:.7rem 0 0; font-size:.85rem; }

.staff-card { border-left:3px solid var(--rdy-amber); }
.staff-clear { border-left-color:var(--rdy-green); }
.staff-head { display:flex; align-items:baseline; gap:1rem; flex-wrap:wrap; margin-bottom:.7rem; }
.staff-head h2 { margin:0; }
.staff-tots { display:flex; gap:.9rem; flex-wrap:wrap; align-items:baseline; margin-left:auto; font-size:.85rem; }
.staff-tot b { font-size:1.05rem; font-variant-numeric:tabular-nums; }
.staff-tot.staff-open b { color:var(--rdy-red); }
.staff-tot.staff-unconf b { color:var(--rdy-amber); }
.staff-list { list-style:none; margin:0; padding:0; }
.staff-i + .staff-i { border-top:1px solid var(--border); }
.staff-i a { display:flex; align-items:center; gap:.8rem; padding:.55rem .3rem; text-decoration:none; color:inherit; }
.staff-i a:hover { background:rgba(120,140,170,.08); border-radius:6px; }
.staff-when { flex:none; width:6.2em; font-size:.82rem; font-weight:700; text-transform:uppercase; letter-spacing:.03em; color:var(--muted); }
.staff-now .staff-when { color:var(--rdy-red); }
.staff-soon .staff-when { color:var(--rdy-amber); }
.staff-name { font-weight:600; }
.staff-name .evno { font-family:ui-monospace,Consolas,monospace; font-size:.78rem; color:var(--muted); }
.staff-need { margin-left:auto; display:flex; gap:.4rem; flex-wrap:wrap; justify-content:flex-end; }
.staff-pill { font-size:.74rem; font-weight:700; padding:.15em .6em; border-radius:20px; white-space:nowrap; }
.staff-pill.staff-open { background:rgba(214,69,69,.16); color:#e88; }
.staff-pill.staff-unconf { background:rgba(217,138,16,.16); color:#e5b45a; }
.staff-more { margin:.7rem 0 0; font-size:.85rem; }

/* ============================ Messages ============================ */
.chat { display:grid; grid-template-columns:240px 1fr; gap:1rem; align-items:start; }
@media (max-width:760px) { .chat { grid-template-columns:1fr; } }
.chat-side { background:var(--panel); border:1px solid var(--border); border-radius:10px; padding:.5rem; max-height:70vh; overflow-y:auto; }
.chat-grp { font-size:.7rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); padding:.6rem .5rem .25rem; }
.chat-ch { display:flex; align-items:center; gap:.4rem; padding:.4rem .55rem; border-radius:7px; color:var(--text); font-size:.88rem; }
.chat-ch:hover { background:var(--panel-2); text-decoration:none; }
.chat-ch.is-on { background:var(--accent-2); color:#fff; }
.chat-ch-n { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.chat-unread { margin-left:auto; background:var(--red); color:#fff; border-radius:20px; font-size:.68rem; font-weight:700; padding:0 .45em; }
.chat-main { display:flex; flex-direction:column; min-height:60vh; margin-bottom:0; }
.chat-head { display:flex; justify-content:space-between; align-items:center; gap:1rem; padding-bottom:.6rem; margin-bottom:.6rem; border-bottom:1px solid var(--border); }
.chat-log { flex:1; overflow-y:auto; max-height:52vh; padding-right:.3rem; }
.msg { margin-bottom:.7rem; }
.msg-who { font-size:.74rem; color:var(--muted); margin-bottom:.1rem; }
.msg-body { background:var(--panel-2); border-radius:12px; padding:.45rem .7rem; display:inline-block; max-width:88%; }
.msg.mine { text-align:right; }
.msg.mine .msg-body { background:var(--accent-2); color:#fff; text-align:left; }
.chat-form { margin-top:.7rem; border-top:1px solid var(--border); padding-top:.7rem; }
.chat-form textarea { width:100%; padding:.6em .7em; border:1px solid var(--border); border-radius:9px; background:var(--panel-2); color:var(--text); font:inherit; }
.chat-actions { display:flex; gap:.5rem; margin-top:.5rem; flex-wrap:wrap; }

/* ============================ Opeerion identity ============================
   Applied on top of the component layer above so the whole app reads as one
   system: soft hero light behind the page, brand-tinted card edges, and the
   ramp used sparingly as an accent rather than a fill.
   ========================================================================= */

/* Soft brand light behind the page, matching the site's hero gradient. */
body::before {
  content: '';
  position: fixed; inset: 0 0 auto 0; height: 420px;
  background:
    radial-gradient(1100px 320px at 12% -12%, #0258d326, transparent 70%),
    radial-gradient(900px 300px at 88% -18%, #e13dea1f, transparent 70%);
  pointer-events: none; z-index: 0;
}

/* Stacking order, and it matters. The page body is lifted above the fixed glow
   above; the header is lifted above the page body, because its dropdowns are
   children of the header and cannot escape its stacking context.
   Do NOT add .topbar back into the .wrap rule: giving it the same z-index as
   .wrap put the menus behind the page content, since .wrap comes later in the
   document and therefore won. Nor set `isolation` on .topbar, which traps the
   menus in a second context for the same result.

   .wrap must also outrank .foot, and for the same reason in reverse. They were
   both z-index:1, and on a tie the later element in the document wins — so the
   footer painted over the page. Anything the page opens downwards out of flow
   (an edit popover from a row near the bottom) reached the footer and was
   covered by it: visible, but swallowing every click below the footer's top
   edge. A popover's own z-index cannot rescue it, because inside .wrap's
   stacking context it only ever counts as .wrap's level. Both still clear the
   glow at 0. */
.wrap { position: relative; z-index: 2; }
.foot { position: relative; z-index: 1; }
.topbar { z-index: 60; }

.card { box-shadow: 0 1px 0 var(--tint-2) inset; }
.foot { border-top: 1px solid var(--border); letter-spacing: .01em; }

/* Chips and pagers pick up the brand rather than the old flat blue. */
.chip-on { border-color: #7b2fd6aa; background: #7b2fd61f; }
.pager .current { background: var(--accent-2); border-color: transparent; color: #fff; }

/* Inputs: brand focus ring. */
input:focus, select:focus, textarea:focus {
  /* The lip is kept and the ring added, so focusing a field changes its colour
     rather than its depth. */
  border-color: var(--focus-ring); box-shadow: var(--control-inset), 0 0 0 3px var(--focus-halo);
}

/* Flash messages, restated on ink. */
.flash-ok    { background: #06251a; color: #6ee7a8; border: 1px solid #12563a; }
.flash-error { background: #2a0f12; color: #ffa1a1; border: 1px solid #6b2530; }

/* Print: drop the textures and lights so quotes/invoices/show books stay clean. */
@media print {
  body { background: #fff; color: #000; background-image: none; }
  body::before { display: none; }
  .topbar, .subnav, .foot { display: none; }
}

/* ==================== Show intake & list ==================== */

/* Service picker on the new-show page: the workflows a show is built from. */
.wf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: .6rem; }
.wf-item {
  display: flex; gap: .6rem; align-items: flex-start;
  padding: .7rem .8rem; border: 1px solid var(--border); border-radius: 10px;
  background: var(--tint-1); cursor: pointer; transition: border-color .14s, background-color .14s;
}
.wf-item:hover { background: var(--tint-3); border-color: var(--border-strong); }
.wf-item input { width: auto; margin-top: .15rem; flex: none; }
.wf-item strong { display: block; font-size: .92rem; font-weight: 550; }
.wf-item small { display: block; color: var(--muted); font-size: .78rem; line-height: 1.35; margin-top: .1rem; }
/* :has is well supported; without it the checkbox alone still reads as chosen. */
.wf-item:has(input:checked) { background: #7b2fd61f; border-color: #7b2fd6aa; }

/* Show rows: make the whole title read as the link it is. */
.rows-linked tbody tr:hover { background: var(--tint-1); }
.show-link { display: inline-flex; align-items: baseline; gap: .5rem; color: var(--text); }
.show-link:hover { text-decoration: none; }
.show-link .show-name {
  font-weight: 600;
  /* An underline that only appears on hover reads as a link without shouting. */
  background: var(--accent-bar) bottom left / 0% 1.5px no-repeat;
  transition: background-size .18s;
  padding-bottom: 1px;
}
.show-link:hover .show-name, .rows-linked tr:hover .show-name { background-size: 100% 1.5px; }
.show-link:hover .show-name { color: #fff; }

/* Status changed straight from the list. */
.status-pick {
  width: auto; padding: .2em 1.6em .2em .6em; font-size: .78rem; font-weight: 600;
  border-radius: 99px; cursor: pointer;
}

/* A quote line's service, shown beside its description. */
.wf-tag {
  display: inline-block; margin-left: .4rem; padding: .05em .5em;
  border-radius: 99px; font-size: .7rem; font-weight: 600;
  background: #7b2fd626; border: 1px solid #7b2fd655; color: #d2b6ff;
  vertical-align: middle;
}

/* Rooms proposed from a parsed grid, awaiting review. Least-certain first,
   tinted so the rows needing a second look are obvious at a glance. */
.prop-low  td { background: #e0a93f14; }
.prop-low  td:first-child { box-shadow: inset 3px 0 0 var(--amber); }
.prop-medium td:first-child { box-shadow: inset 3px 0 0 var(--tint-7); }
.prop-sub td { border-top: none; padding-top: 0; }
.conf-low, .conf-medium { font-size: .72rem; margin-top: .2rem; }
.conf-low { color: var(--amber); }
.conf-medium { color: var(--muted); }
#proposals input { font-size: .85rem; padding: .3em .5em; }

/* --- AI is working ------------------------------------------------------
   Every AI action here is a synchronous request that can run for the better
   part of a minute. Without this the page is indistinguishable from one that
   ignored the click, which is how we taught people to click twice and pay
   twice. The bar is indeterminate on purpose: the API streams us no progress,
   so a percentage would be invented. The elapsed count is real. */
.ai-busy { margin-top: .8rem; }
.ai-busy-track {
  height: 4px; border-radius: 999px; background: var(--tint-6);
  overflow: hidden; position: relative;
}
.ai-busy-track i {
  position: absolute; inset: 0 auto 0 0; width: 38%; border-radius: 999px;
  background: var(--accent-bar);
  animation: ai-slide 1.4s cubic-bezier(.45,.05,.55,.95) infinite;
}
@keyframes ai-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(263%); }
}
.ai-busy-note { margin: .45rem 0 0; font-size: .82rem; color: var(--muted); }

/* Motion is decoration here — the elapsed count carries the meaning, so it
   still reads as working with the animation switched off. */
@media (prefers-reduced-motion: reduce) {
  .ai-busy-track i { animation: none; width: 100%; opacity: .55; }
}

/* --- Search -------------------------------------------------------------
   The topbar box is always there; results are grouped by what kind of thing
   matched, because "Hilton" is a venue and also the shows at it, and one
   merged list is no use to anyone. */
.topsearch {
  display: flex; align-items: center; gap: .4rem; margin-left: auto;
  padding: .32rem .6rem; border: 1px solid var(--border); border-radius: 999px;
  background: var(--tint-2); color: var(--muted); min-width: 0;
}
.topsearch:focus-within { border-color: var(--border-strong); color: var(--text); }
.topsearch svg { flex: none; }
.topsearch input {
  border: 0; background: none; padding: 0; margin: 0; color: inherit;
  font: inherit; font-size: .86rem; width: 8.5rem; min-width: 0;
}
.topsearch input:focus { outline: none; width: 12rem; }
.topsearch input::placeholder { color: var(--muted); }
.topsearch input::-webkit-search-cancel-button { filter: invert(.6); }

.search-h { display: flex; align-items: baseline; gap: .55rem; margin-bottom: .5rem; }
.search-h .muted { font-weight: 400; font-size: .85rem; }
.search-all { margin-left: auto; font-size: .82rem; font-weight: 500; }

.search-list { list-style: none; margin: 0; padding: 0; }
.search-list li {
  display: flex; align-items: baseline; gap: .6rem;
  padding: .5rem 0; border-top: 1px solid var(--border);
}
.search-list li:first-child { border-top: none; }
.search-list a { display: flex; flex-wrap: wrap; align-items: baseline; gap: .55rem; min-width: 0; }
.search-t { font-weight: 600; }
.search-s { color: var(--muted); font-size: .86rem; }
.search-list mark { background: none; color: var(--accent); font-weight: 700; }
.search-list .badge { margin-left: auto; flex: none; }
.search-what { columns: 2; font-size: .88rem; }

/* Crew and gear have no page of their own, so search links at a row. Say
   which row it meant rather than dropping the reader at a table. */
tr:target td { background: #0258d31f; box-shadow: inset 3px 0 0 var(--blue); }

@media (max-width: 860px) {
  .topsearch { order: 3; width: 100%; margin: .5rem 0 0; border-radius: 10px; }
  .topsearch input, .topsearch input:focus { width: 100%; }
  .search-what { columns: 1; }
}

/* --- The as-you-type dropdown ------------------------------------------ */
.topsearch { position: relative; }
.topsearch-menu {
  position: absolute; top: calc(100% + .5rem); right: 0; z-index: 60;
  width: min(26rem, calc(100vw - 2rem));
  max-height: min(70vh, 32rem); overflow-y: auto;
  background: var(--ink-soft, #0a0d15); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 12px;
  box-shadow: 0 18px 44px #00000073;
  padding: .35rem;
}
/* The header Clem asked for: which part of the system a block came from. */
.ts-h {
  display: flex; align-items: baseline; gap: .45rem;
  margin: .5rem .55rem .25rem; padding-bottom: .28rem;
  border-bottom: 1px solid var(--border);
  font-size: .72rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted);
}
.ts-h span { font-weight: 500; letter-spacing: 0; }
.ts-h:first-child { margin-top: .25rem; }

.ts-row {
  display: flex; align-items: baseline; gap: .5rem;
  padding: .42rem .55rem; border-radius: 8px; color: var(--text);
}
.ts-row:hover, .ts-row.ts-on { background: var(--tint-6); }
.ts-t { font-weight: 600; font-size: .88rem; }
.ts-s { color: var(--muted); font-size: .8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ts-m { margin-left: auto; flex: none; color: var(--muted); font-size: .72rem; }
.topsearch-menu mark { background: none; color: var(--accent); font-weight: 700; }

.ts-all {
  display: block; margin-top: .3rem; padding: .5rem .55rem;
  border-top: 1px solid var(--border); border-radius: 0 0 8px 8px;
  font-size: .82rem; font-weight: 500;
}
.ts-all:hover, .ts-all.ts-on { background: var(--tint-6); }
.ts-none { margin: 0; padding: .7rem .55rem; color: var(--muted); font-size: .85rem; }

@media (max-width: 860px) {
  .topsearch-menu { left: 0; right: 0; width: auto; }
}

/* A heading with a button cluster on the right.
   Previously done with float:right, which does not grow the heading — so the
   moment the buttons wrapped onto a second line, whatever followed slid
   underneath them. A flex heading keeps its own height however the buttons
   wrap, on any width. */
h2.h2-row, h3.h2-row {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem;
}
.h2-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin-left: auto; }
@media (max-width: 640px) { .h2-actions { margin-left: 0; width: 100%; } }

/* === Phones ==============================================================
   Two causes accounted for almost all of it, measured across ten rendered
   pages at 375px:

   1. Grid and flex children default to min-width:auto, which means they refuse
      to shrink below their content. A two-column form grid with ordinary text
      inputs, or a 3fr/2fr page split holding a table, therefore pushed the
      whole page sideways however narrow the screen got.
   2. Tables had no scroll container. Reports and Book Crew were the only two
      pages that wrapped theirs, and they were the only two of the ten that did
      not overflow — a 1549px grid sat happily inside 375px there while a 511px
      venue table shoved the page 177px wide.

   So: let things shrink, and give every table somewhere to scroll. */

/* Nothing may shrink-lock a layout open. */
.form-grid > *, .cols > *, .cols-even > *, .cols-match > *,
.page-head > *, .card > * { min-width: 0; }
/* break-word, not anywhere, and the difference is the whole of this bug.
   Both break a word that would otherwise overflow, and visually they are the
   same inside a plain block. But `anywhere` also counts when the browser works
   out a box's *minimum* width -- under `anywhere` the minimum is one character.
   Inside a table that is fatal: a column of names sitting beside columns of
   fixed-width number inputs is told it can live in 50px, and "Assistant Stage
   Manager (ASM)" comes down the page a syllable at a time. `break-word` keeps
   the longest word as the minimum, so the column holds its ground, and a
   genuinely unbreakable string still breaks rather than overflowing.

   The reason this is safe on a phone -- which is what `anywhere` was reaching
   for -- is that every table now scrolls inside its own container, so a wide
   column scrolls instead of shoving the page sideways. */
/* Where a table is wider than the card holding it. The table keeps its own
   layout and its full width, so headings stay over their data; only the view
   of it moves. forms.js puts every .table inside one of these. */
.table-scroll { overflow-x: auto; max-width: 100%; }

.card, .table td { overflow-wrap: break-word; }
/* Data may break mid-word to fit; a column heading may not. "Quotes" was
   coming out as one letter per line when the column got squeezed. */
.table th { overflow-wrap: normal; }
input, select, textarea { max-width: 100%; }
/* Wants to be roomy, but never wider than the screen it is on. A fixed
   min-width beats max-width, which is how a 340px search box broke a
   320px phone. */
.input-wide { width: 100%; max-width: 22rem; }

@media (max-width: 760px) {
  .wrap { padding: 1rem .8rem 2.5rem; }

  /* Two columns of form need about 640px to be readable, but every other
     mobile rule starts at 760 — which left a band where a form still ran two
     up while its rows had started wrapping. One column from here down. */
  .form-grid { grid-template-columns: 1fr; }
  .form-grid .span2 { grid-column: span 1; }

  /* A table scrolls inside itself rather than taking the page with it.
     display:block is what creates the scroll box; the rows keep their own
     table layout inside it, so columns stay aligned. */
  .table { display: block; width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }

  /* Cards that templates open at a fixed width inside a cell or a details
     panel — 300px of card in a 375px screen leaves nothing for padding. */
  .card[style*="min-width"], form[style*="min-width"], div[style*="min-width"] { min-width: 0 !important; }
  .inline-details > form.form-grid { position: static; width: 100%; }

  /* Toolbars written as an inline flex row — 34 of them across the app, mostly
     the button cluster beside a page title. None set flex-wrap, so five buttons
     stayed on one line and took the page with them. On a phone, wrapping is
     always what was meant. Mobile-only, so desktop rows are untouched. */
  [style*="display:flex"], [style*="display: flex"] { flex-wrap: wrap; }

  /* Little inline edit forms — a couple of number boxes and a Save — sit in
     table cells. Once a table stacks, the cell is a block and the form has to
     be able to wrap inside it rather than run past the edge. */
  form.inline { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
}

@media (max-width: 640px) {

  .page-head { flex-direction: column; align-items: stretch; gap: .5rem; }
  /* The label still has to be told to sit in the middle of the width it now
     has, and Collapse all has to give up the margin that separated it from a
     button no longer beside it.

     Most page heads keep their controls in a flex row of their own, and that
     row carries an inline align-items: flex-start -- which a stylesheet cannot
     outrank. Giving the children an explicit width sidesteps the fight
     entirely rather than reaching for !important. */
  .page-head > div:not(:first-child) { flex-direction: column; width: 100%; }
  .page-head > div:not(:first-child) > .btn,
  .page-head > div:not(:first-child) > details,
  .page-head > div:not(:first-child) > details > .btn { width: 100%; }

  .page-head > .btn,
  .page-head > .collapse-all,
  .page-head > div:not(:first-child) > .btn,
  .page-head > div:not(:first-child) > details > .btn { text-align: center; }
  .page-head > .collapse-all { margin-left: 0; }
  .table { font-size: .86rem; }

  /* Stacked rows: one record per block, each cell labelled with its column.
     Only applied to tables the script has labelled, so with JavaScript off
     the horizontal scroll above is still the behaviour and nothing is left
     unlabelled and meaningless. */
  .table.tbl-stack { display: block; overflow: visible; }
  .table.tbl-stack thead { display: none; }
  .table.tbl-stack tbody, .table.tbl-stack tr, .table.tbl-stack td { display: block; width: auto; }
  .table.tbl-stack tr {
    border: 1px solid var(--border); border-radius: 10px;
    padding: .5rem .7rem; margin-bottom: .6rem; background: var(--tint-1);
  }
  .table.tbl-stack td { border: none; padding: .22rem 0; text-align: left; }
  .table.tbl-stack td:empty { display: none; }
  .table.tbl-stack td::before {
    content: attr(data-label);
    display: block; font-size: .68rem; font-weight: 700;
    letter-spacing: .06em; text-transform: uppercase; color: var(--muted);
  }
  /* The first cell is the record's name — it heads the block, unlabelled. */
  .table.tbl-stack td:first-child { font-weight: 600; font-size: 1rem; padding-bottom: .35rem; }
  .table.tbl-stack td:first-child::before { display: none; }
  .table.tbl-stack td.num { text-align: left; }
  /* Stacked rows have no columns to align to, so the links just sit in a row. */
  .table.tbl-stack td.actions { display: flex; flex-wrap: wrap; gap: .8rem; }
  .table.tbl-stack td.actions > * { margin-right: 0; }
}

/* The access picker and its Set button belong on one line. Squeezed into a
   column that had to share with six others, the select clipped "Administrator"
   mid-word and the button dropped underneath it. */
.role-pick { display: flex; gap: .4rem; align-items: center; flex-wrap: nowrap; }
.role-pick select { min-width: 11rem; }
.table td.actions .inline-details > form.form-grid { min-width: 300px; }

/* Starting a show from a previous one. */
.repeat-pick { margin-bottom: 1rem; }
.repeat-pick.repeat-on { border-color: var(--accent); }
/* Last year's write-up: quoted rather than editable, so it does not read as
   another box somebody is meant to fill in. */
.lastnotes {
  border-left: 3px solid var(--note-line); padding: .1rem 0 .1rem .8rem;
  color: var(--text); font-size: .92rem; white-space: normal;
}

/* Looking at the app as another role. Deliberately unlike anything else on
   the page -- it is the one banner that must not read as part of the app. */
.simbar {
  position: sticky; top: 0; z-index: 200;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap;
  padding: .5rem 1rem;
  background: var(--warn-bg); color: var(--warn-fg);
  border-bottom: 2px solid var(--warn-line);
  font-size: .88rem;
}
.simbar-t { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; }
.simbar-h { opacity: .8; font-size: .8rem; }
.simbar .btn { background: var(--warn-fg); color: var(--warn-bg); border-color: var(--warn-fg); }
.simbar .btn:hover { opacity: .9; }

/* ============================ Report an issue ============================ */
/* Wider than a quick-add: a capture of a whole page is unreadable at 560px and
   the point of showing it is that somebody can actually check what it caught. */
.rp-card { max-width: 720px; }
.rp-shots { display: flex; flex-wrap: wrap; gap: .6rem; }
.rp-shot {
  margin: 0; width: 200px;
  border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden; background: var(--tint-1);
}
.rp-shot img { display: block; width: 100%; height: 124px; object-fit: cover; object-position: top; }
.rp-shot figcaption { font-size: .72rem; color: var(--muted); padding: .25rem .45rem 0; }
.rp-drop {
  display: block; width: 100%; border: 0; border-top: 1px solid var(--border);
  background: none; color: var(--muted); font: inherit; font-size: .74rem;
  padding: .3rem; cursor: pointer;
}
.rp-drop:hover { color: var(--bad-fg); background: var(--bad-bg); }
.rp-status { font-size: .85rem; margin: 0; }
.rp-status.rp-ok   { color: var(--ok-fg); }
.rp-status.rp-warn { color: var(--warn-fg); }
/* The menu entry sits among forms that each hold one button, so it borrows
   their shape rather than inventing a third kind of row. */
.navmenu-theme-b {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; font: inherit; color: inherit; padding: .5rem .8rem;
}
.navmenu-theme-b:hover { background: var(--tint-3); }

/* ============================ Quick-add modals (New show) ============================ */
.req { font-size:.62rem; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
  color:var(--accent); vertical-align:middle; margin-left:.15rem; }
body.qa-lock { overflow:hidden; }
.qa-modal { position:fixed; inset:0; z-index:1100; display:flex; align-items:flex-start;
  justify-content:center; padding:6vh 1rem 2rem; overflow-y:auto; }
.qa-modal[hidden] { display:none; }
.qa-backdrop { position:fixed; inset:0; background:rgba(2,4,10,.66); backdrop-filter:blur(2px); }
.qa-card { position:relative; z-index:1; width:100%; max-width:560px; background:var(--panel-2);
  border:1px solid var(--border); border-radius:var(--radius); padding:1.1rem 1.2rem 1.2rem;
  box-shadow:0 24px 60px rgba(0,0,0,.5); }
.qa-head { display:flex; align-items:center; justify-content:space-between; margin-bottom:.2rem; }
.qa-head h2 { margin:0; }
.qa-x { background:none; border:none; color:var(--muted); font-size:1.5rem; line-height:1;
  cursor:pointer; padding:0 .2rem; }
.qa-x:hover { color:var(--text); }
.qa-actions { display:flex; gap:.6rem; align-items:center; margin-top:.4rem; }
/* The permanent New-show button lives in the Shows menu too; drop it on narrow bars. */
@media (max-width:720px){ .newshow-btn { display:none; } }

/* ============================ Ticket fixes (Opeerion ONE) ============================ */
/* #31 / #33 — title blocks read in Title Case; the muted sub-text is left alone. */
.page-head h1 { text-transform: capitalize; }
.page-head h1 .muted, .page-head h1 small { text-transform: none; }

/* #27 — the rate-card holiday forms wrap instead of overlapping on narrow screens. */
.hol-form { display: flex; flex-wrap: wrap; gap: .6rem; align-items: end; }
.hol-form > label { display: flex; flex-direction: column; gap: .2rem; }
.hol-form .hol-name { flex: 1 1 12rem; }
.hol-form .hol-note { align-self: center; }
@media (max-width: 560px) {
  .hol-form { flex-direction: column; align-items: stretch; }
  .hol-form > label, .hol-form > label input, .hol-form > .btn { width: 100%; }
  .hol-form > label input[style] { width: 100% !important; }
}

/* #32 — the calendar month jump, styled like the small nav buttons. */
.cal-jump { background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
  border-radius: 8px; padding: .3rem .5rem; font: inherit; font-size: .82rem; }

/* #34 — the board's "Add to a show" card: two compact forms side by side. */
.addto-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem 1.4rem; }
@media (max-width: 640px) { .addto-grid { grid-template-columns: 1fr; } }
.addto-form { display: flex; flex-wrap: wrap; gap: .6rem; align-items: end; }
.addto-form > label { display: flex; flex-direction: column; gap: .2rem; flex: 1 1 9rem; }
.addto-form .addto-note { flex-basis: 100%; margin: 0; font-size: .78rem; }

/* #30 — show-numbering settings form. */
.num-form { display: flex; flex-wrap: wrap; gap: .8rem 1.2rem; align-items: end; }
.num-form > label { display: flex; flex-direction: column; gap: .2rem; }
.num-form .num-example { align-self: center; font-size: .9rem; color: var(--muted); }
.num-form .num-example strong { color: var(--text); font-family: ui-monospace, Consolas, monospace; }

/* A room reads as a place rather than a service, so it takes the blue end of
   the ramp — distinguishable at a glance when a line carries both tags. */
.wf-tag.room-tag { background:#0258d326; border-color:#0258d366; color:#a8c6ff; }

/* Suggestions on the board are draggable like the rails, and carry a way to
   ask the person first — the contact links must not start a drag themselves. */
.suggest-chip { cursor: grab; }
.suggest-chip:active { cursor: grabbing; }
.suggest-chip.dragging { opacity: .45; }
.suggest-contact { margin-left: .4rem; display: inline-flex; gap: .35rem; }
.suggest-contact a { font-size: .72rem; color: var(--accent-2); text-decoration: underline; cursor: pointer; }

/* ============================ Crew profiles ============================ */
.cp-head { display: flex; gap: 1.1rem; align-items: flex-start; }
.cp-photo { width: 92px; height: 92px; border-radius: 14px; object-fit: cover; flex: none;
  border: 1px solid var(--border); background: var(--panel-2); }
.cp-photo-empty { display: grid; place-items: center; font-size: 1.6rem; font-weight: 700; color: var(--muted); }
.cp-notes { margin-top: 1rem; display: flex; flex-direction: column; gap: .7rem; }
.cp-note { border-left: 2px solid var(--border); padding: .1rem 0 .1rem .7rem; }
.cp-note-head { display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; font-size: .78rem; margin-bottom: .15rem; }

/* Roster search: rows of controls that wrap rather than squeezing. */
.crew-filter { margin-bottom: 1rem; }
.cf-row { display: flex; flex-wrap: wrap; gap: .6rem .8rem; align-items: end; }
.cf-row + .cf-row { margin-top: .6rem; }
.cf-row > label { display: block; font-size: .78rem; color: var(--muted); }
.cf-row > label > input, .cf-row > label > select { display: block; margin-top: .2rem; }
/* A date box carries an intrinsic minimum width of its own. Without these it
   refuses to shrink and refuses to wrap, so it runs over the box beside it. */
.cf-row > label { min-width: 0; }
.cf-row > label > input[type=date] { min-width: 0; width: 9.5rem; max-width: 100%; }
.cf-grow { flex: 1 1 20rem; }
.crew-result { margin: -.4rem 0 .8rem; font-size: .88rem; }
.crew-result .warn { color: var(--rdy-amber, #d98a10); }
.crew-name { display: inline-flex; align-items: center; gap: .5rem; }
.crew-thumb { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; flex: none;
  border: 1px solid var(--border); background: var(--panel-2); }
.crew-thumb-empty { display: grid; place-items: center; font-size: .68rem; font-weight: 700; color: var(--muted); }

/* =========================================================================
   Preferences
   =========================================================================
   Two sources, in this order of trust:

   1. What the operating system already knows. Someone who turned motion down
      in their OS did it once, years ago, system-wide. They should not have to
      find a switch in here as well, and most never will -- so these apply with
      no interface at all and no discovery problem to solve.

   2. What this person told us, on the Account page. The second way in, for the
      many people who have never heard of the first.

   Nothing here changes anything for anyone who asked for nothing. Every rule
   sits behind a media query or a data- attribute, and the defaults are the
   values the app has always had.
   ========================================================================= */

/* The preference rows on the Account page. A row per switch, its explanation
   beside it rather than under a label, because these need a sentence to be
   worth choosing. */
.prefs { display: grid; gap: .75rem; margin: 1rem 0; }
.pref {
  display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap;
  border: 1px solid var(--border); border-radius: 10px;
  padding: .9rem 1rem; margin: 0;
}
.pref > div { flex: 1 1 22rem; min-width: 0; }
.pref strong { display: block; color: var(--text); }
.pref p { margin: .2rem 0 0; font-size: .85rem; max-width: 60ch; }
.pref .btn { flex: none; align-self: center; }

/* ---- 2.5.8 Target Size --------------------------------------------------
   Four controls are smaller than the 24x24 the criterion asks for: the delete
   crosses (.linklike at 19.2px, .cov-x at 18, .pick-x at 15.2) and the Add
   button added for people who cannot drag (.chip-add at 19.4).

   Growing them would push every table row taller and change a layout that
   works, so the hit area grows instead of the control. An absolutely positioned
   pseudo-element is outside the flow, so it costs nothing in layout and nothing
   visually -- the glyph stays exactly the size it is, and the area you can hit
   with a shaking hand or a thumb becomes the square the standard asks for. */
.linklike, .cov-x, .pick-x, .chip-add { position: relative; }
.linklike::after,
.cov-x::after,
.pick-x::after,
.chip-add::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 24px; height: 24px;
  transform: translate(-50%, -50%);
  /* Behind the glyph so it never covers the thing it is enlarging, and
     invisible either way. */
  z-index: 0;
}

/* The key/value tables inside an asset's details panel: dense, and the label
   column only as wide as it needs to be. */
.asset-kv { font-size: .82rem; margin: 0; }
.asset-kv td { padding: .18rem .5rem .18rem 0; border-bottom: 1px solid var(--border); vertical-align: top; }
.asset-kv td:first-child { white-space: nowrap; width: 1%; }
.asset-detail { max-height: 26rem; overflow-y: auto; }

/* ---- Motion -------------------------------------------------------------
   The stylesheet carries twenty transitions and animations; the handful of
   rules further up turned off six of them. Someone who asked for stillness got
   fourteen moving things anyway. This covers everything, because the point is
   the absence of movement rather than a curated subset of it.

   Near-zero rather than `none`: a duration of 0 stops transitionend from ever
   firing, and script that waits for it would hang. This keeps the event and
   removes the motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
:root[data-motion="still"] *,
:root[data-motion="still"] *::before,
:root[data-motion="still"] *::after {
  animation-duration: .01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: .01ms !important;
  scroll-behavior: auto !important;
}

/* ---- More contrast ------------------------------------------------------
   The answer to a question left open on purpose: the decorative hairlines are
   faint, and lifting them for everyone would change a design that people like.
   So they lift for the people who asked their system for more contrast, and
   for nobody else. Only tokens are touched, so every component follows without
   a rule of its own. */
@media (prefers-contrast: more) {
  :root {
    --border: #ffffff57;
    --border-strong: #ffffff80;
    --muted: #b6bdca;
    --muted-deep: #98a0ad;
  }
  :root[data-theme="light"] {
    --border: #0f172a78;
    --border-strong: #0f172aa0;
    --muted: #3d4657;
    --muted-deep: #4a5464;
  }
  /* A row that is only tinted becomes a row that is also outlined. */
  tr.row-bad > td { box-shadow: inset 2px 0 0 var(--red); }
}

/* ---- Less transparency --------------------------------------------------
   Translucent surfaces put text over whatever happens to be behind it, which is
   unreadable for some people and nauseating for others. Where a surface is
   translucent, make it a surface. */
@media (prefers-reduced-transparency: reduce) {
  /* The sticky top bar is 88% panel over a blur; the quick-add backdrop is a
     wash over the page. Both become solid. */
  .topbar { background: var(--panel) !important; backdrop-filter: none !important; }
  .qa-backdrop { background: #000000e6 !important; backdrop-filter: none !important; }
  /* Something being dragged is half-faded; keep it whole. */
  .kcard.kdrag, .rail-chip.dragging { opacity: 1 !important; }
}

/* ---- Calm ---------------------------------------------------------------
   Off by default. The dense layout is right for someone who spends all day in
   this and knows where everything is; it is punishing for someone who does not,
   or who is having a day where everything is loud.

   Three things, and deliberately not a fourth: room to breathe, status colour
   turned down so one badge stops competing with the next, and the decorative
   flourishes stilled. No content is removed -- hiding things is how people lose
   track of them, which is the opposite of the help intended. */
:root[data-calm="1"] {
  --radius: 10px;
}
:root[data-calm="1"] .card { padding: 1.5rem 1.6rem; margin-bottom: 1.5rem; }
:root[data-calm="1"] .table th { padding: .7rem .75rem; }
:root[data-calm="1"] .table td { padding: .85rem .75rem; }
:root[data-calm="1"] .form-grid { gap: 1.15rem; }
:root[data-calm="1"] body { line-height: 1.72; }
:root[data-calm="1"] p, :root[data-calm="1"] li { max-width: 74ch; }

/* Status still readable, no longer shouting. Saturation only -- the hue stays,
   so a colour someone has learned still means what it meant. */
:root[data-calm="1"] .badge,
:root[data-calm="1"] .chip,
:root[data-calm="1"] [class*="sh-bar-"],
:root[data-calm="1"] [class*="as-"],
:root[data-calm="1"] [class*="mt-"] { filter: saturate(.62); }

/* The gradient is the brand, and on a calm setting it is also the loudest thing
   on the page. Kept, quietened. */
:root[data-calm="1"] .btn-primary { filter: saturate(.72); }

/* The two things in the app that move on their own: the menu that pops open,
   and the barber-pole that runs while the AI is thinking. Calm keeps both
   present and stops them animating. */
:root[data-calm="1"] .navmenu { animation: none !important; }
:root[data-calm="1"] .ai-busy-track i { animation: none !important; width: 100%; opacity: .55; }


/* ======================== Inventory browser ============================
   A filing cabinet: the tree on the left keeps its place while the panel on
   the right changes. On a narrow screen there is no room for two columns, so
   the tree sits above the panel and stops being sticky -- a sticky drawer on a
   phone is a drawer covering the thing you opened. */
.gear-browser { display:grid; grid-template-columns: minmax(240px, 320px) 1fr; gap:1.25rem; align-items:start; }
@media (max-width: 900px) { .gear-browser { grid-template-columns: 1fr; } }

.gear-tree { position:sticky; top:1rem; max-height:calc(100vh - 2rem); overflow:auto; padding:.85rem; }
@media (max-width: 900px) { .gear-tree { position:static; max-height:26rem; } }

.gear-find { display:flex; gap:.4rem; margin-bottom:.6rem; }
.gear-find input[type=search] { flex:1 1 auto; min-width:0; }
.gear-tree-note { font-size:.8rem; margin:.2rem 0 .5rem; }
.gear-root-note { font-size:.78rem; margin:.1rem 0 .45rem .9rem; }

/* Every level is a link and the open path is in the URL, so following a node is
   what opens it -- there is no drawer to open separately, and the browser's back
   button walks back up the tree. */
.gear-tier { list-style:none; margin:0; padding:0; }
.gear-tier .gear-tier { margin-left:.8rem; border-left:1px solid var(--border); padding-left:.35rem; }

.gear-node-link {
  display:flex; align-items:center; gap:.35rem; text-decoration:none; color:var(--text);
  padding:.28rem .35rem; border-radius:6px; font-size:.86rem;
}
.gear-node-link:hover { background:var(--panel-2); }
.gear-node-link:focus-visible { outline:2px solid var(--focus-ring); outline-offset:1px; }
.gear-node-link.is-open { font-weight:600; }
.gear-root-link { font-size:.92rem; font-weight:600; }

.gear-caret { flex:0 0 auto; width:.7rem; font-size:.6rem; color:var(--muted); line-height:1; }
.gear-node-name { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.gear-n { flex:0 0 auto; font-size:.76rem; font-variant-numeric:tabular-nums; }

.gear-root-note { font-size:.78rem; margin:.1rem 0 .45rem 1.05rem; }
.gear-leaves { list-style:none; margin:.1rem 0 .3rem .8rem; padding:0 0 0 .35rem; border-left:1px solid var(--border); }
.gear-leaf-sub { font-size:.78rem; }

.gear-leaf {
  display:block; padding:.24rem .4rem; border-radius:5px; font-size:.85rem;
  text-decoration:none; color:var(--text);
}
.gear-leaf:hover { background:var(--panel-2); }
.gear-leaf.is-on { background:var(--panel-2); font-weight:600; }
.gear-leaf:focus-visible { outline:2px solid var(--focus-ring); outline-offset:1px; }

.gear-hits { list-style:none; margin:0; padding:0; }
.gear-hits li { padding:.15rem 0; border-top:1px solid var(--border); }
.gear-hits li:first-child { border-top:none; }
.gear-hit-where { display:block; font-size:.74rem; padding:0 .4rem .15rem; }

.gear-detail .card { margin-bottom:1rem; }
.gear-detail .card:last-child { margin-bottom:0; }
.gear-money .asset-kv td { white-space:nowrap; }
.gear-detail tr.is-on > td { background:var(--panel-2); }
.gear-detail table { display:block; overflow-x:auto; }


/* The dot beside somebody who has changed their own record. Small, and never
   the only signal: it carries a title and hidden text, because a colour alone
   says nothing to a screen reader and little to anyone who cannot separate
   blue from grey. */
.crew-dot {
  display:inline-block; width:.5rem; height:.5rem; border-radius:50%;
  background:var(--info-fg, #4a8fe0); margin-right:.35rem; flex:0 0 auto;
  vertical-align:middle;
}
.crew-changed { border-left:3px solid var(--info-fg, #4a8fe0); }
.crew-changed mark { background:var(--info-bg); color:var(--info-fg); padding:.05em .3em; border-radius:3px; }
.crew-change-notable td:first-child { font-weight:600; }


/* Thirty days of one item, side by side. Wraps rather than scrolling, because
   a strip you have to drag sideways on a phone is a strip nobody reads. */
.gav-strip { display:flex; flex-wrap:wrap; gap:3px; margin:.6rem 0 .2rem; }
.gav-d {
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  min-width:2.6rem; padding:.3rem .2rem; border:1px solid var(--border); border-radius:6px;
  font-size:.72rem; line-height:1.25;
}
.gav-dow { color:var(--muted); font-size:.62rem; text-transform:uppercase; letter-spacing:.04em; }
.gav-num { font-weight:600; }
.gav-left { font-variant-numeric:tabular-nums; font-weight:700; }
.gav-d.is-today { outline:2px solid var(--accent); outline-offset:-2px; }

.page-back { margin-bottom:.35rem; }
.page-back a { text-decoration:none; }
.page-back a:hover { text-decoration:underline; }


/* Find and Clear were being squeezed until "Clear" wrapped to two lines inside a
   box barely wider than the letters. The input is the only thing here that
   should give up width; the buttons are as wide as their words and no wider. */
.gear-find { display:flex; gap:.4rem; align-items:center; flex-wrap:nowrap; }
.gear-find input[type=search] { flex:1 1 auto; min-width:0; }
.gear-find .btn { flex:0 0 auto; white-space:nowrap; }


/* A short menu of places to go, hanging off a button. Wide enough for its
   longest label so nothing wraps, and capped so it cannot outgrow a phone. */
.page-menu {
  display:flex; flex-direction:column; gap:.1rem; padding:.35rem;
  min-width:14rem; max-width:min(20rem, calc(100vw - 2rem)); margin-bottom:0;
}
.page-menu a {
  display:block; padding:.5rem .6rem; border-radius:6px;
  text-decoration:none; color:var(--text); font-size:.88rem; white-space:nowrap;
}
.page-menu a:hover { background:var(--panel-2); }
.page-menu a:focus-visible { outline:2px solid var(--focus-ring); outline-offset:1px; }


/* `hidden` means hidden.
   The browser's own [hidden]{display:none} lives in the user-agent stylesheet,
   so any author rule that sets display -- .pick-item{display:flex}, say --
   silently outranks it, and script that hides a row by setting el.hidden leaves
   it on screen. Stated here so honouring the attribute is not something each
   component has to remember. */
[hidden] { display: none !important; }


/* The scope target sits above the scrolling list, because it mirrors the
   filters directly above it and would be useless once scrolled away. */
.pick-drop-wide {
  margin: 0 0 .6rem; border-style: solid; border-color: var(--border-strong);
  background: var(--panel-2);
}
.pick-drop-wide .pick-drop-l { display: flex; gap: .4rem; align-items: baseline; flex-wrap: wrap; }
.pick-drop-wide.drop-on { border-color: var(--accent); }


/* A caption that belongs beside the field name, not under it. The blanket rule
   that makes spans in a form label into blocks is right for a hint that wants
   its own line and wrong for a word that finishes the label. */
.form-grid label > span.k-qty-lbl { display: inline; margin-top: 0; }

/* A tick box, its sentence, and the paragraph explaining it. The first two are
   one line; the third is not, and squeezing it into the same flex row left it
   in a column narrower than its own words. */
.taxlabel { flex-wrap: wrap; }
.taxlabel > small { flex: 1 0 100%; margin-left: 0; padding-left: 1.6rem; }


/* How many, on the card, before you add or drag it.
   Laid out at the end of the card rather than floated over it: the Add button
   alone could sit in the corner without hitting anything, but a number field
   beside it would have covered the category line. And in this list both stay
   visible -- a control that only appears on hover is a control a touch screen
   never shows, and the count has to be set before the drag begins. */
.pick-qty { display: flex; gap: .3rem; align-items: center; align-self: flex-end; margin-top: .2rem; }
.pick-qty-n {
  width: 3.4rem; text-align: center; font-size: .72rem;
  padding: .12em .25em; margin-top: 0;
}
.pick-item .chip-add { position: static; opacity: 1; pointer-events: auto; font-size: .72rem; padding: .18em .6em; }


/* A menu sits over the page, not inside it.
   Opening Tools used to grow the header, so everything below it jumped down --
   which on a page whose whole point is a list you are reading is the one thing
   it must not do. The Add form stays in the flow deliberately: it is a form you
   work through, not a menu you glance at. */
.page-head details:has(> nav.page-menu) { position: relative; }
.page-head nav.page-menu {
  position: absolute; right: 0; top: calc(100% + .4rem); z-index: 60;
  box-shadow: 0 18px 40px rgba(0,0,0,.45);
}
@media (max-width: 640px) {
  /* No room to float on a phone, and nothing below it to disturb. */
  .page-head nav.page-menu { position: static; box-shadow: none; max-width: 100%; }
}


/* A hint on its own line, because a hint inside a label after the control takes
   the bottom of the grid row and pushes the control up -- the boxes then sit at
   different heights and the form looks slapped together. */
.form-grid > .field-hint {
  margin: -.35rem 0 .2rem; font-size: .8rem; color: var(--muted); line-height: 1.45;
}


/* A category row that opens. A button rather than a click handler on the row,
   so it is reachable by keyboard and announces whether it is open. */
.cat-fold {
  display: flex; align-items: center; gap: .45rem; width: 100%;
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: var(--text); text-align: left; cursor: pointer;
  min-height: 0;
}
.cat-fold:hover .cat-caret { color: var(--accent); }
.cat-fold:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-radius: 4px; }
.cat-caret { display: inline-block; width: .7rem; font-size: .7rem; color: var(--muted);
  transition: transform .12s ease; }
.cat-row.is-open .cat-caret { transform: rotate(90deg); }
.cat-n { font-size: .76rem; font-variant-numeric: tabular-nums; }
.cat-leaf { display: inline-flex; align-items: center; padding-left: 1.15rem; }
.sub-row > td:first-child { color: var(--muted); }


/* ===================== Roles and access ================================
   A grid is only worth drawing if two roles can be compared without
   scrolling, so the role and its scope stay put while the columns move. */
.area-tabs { display:flex; gap:.25rem; flex-wrap:wrap; margin:.2rem 0 .9rem;
  border-bottom:1px solid var(--border); }
.area-tab { padding:.45rem .8rem; border-radius:8px 8px 0 0; text-decoration:none;
  color:var(--muted); font-size:.88rem; border:1px solid transparent; border-bottom:none; }
.area-tab:hover { background:var(--panel-2); color:var(--text); }
.area-tab.is-on { background:var(--panel-2); color:var(--text); font-weight:600;
  border-color:var(--border); }

.grid-scroll { overflow-x:auto; }
.perm-grid { min-width:100%; }
.perm-grid th, .perm-grid td { vertical-align:middle; }
.perm-role { position:sticky; left:0; z-index:2; background:var(--panel);
  min-width:14rem; }
.perm-scope { min-width:11rem; font-size:.78rem; }
.perm-col { text-align:center; }
/* Long labels turned the header into a wall of wrapped words; upright keeps a
   dozen columns readable in the width a laptop actually has. */
.perm-col > span { display:inline-block; writing-mode:vertical-rl; transform:rotate(180deg);
  white-space:nowrap; font-weight:600; font-size:.78rem; max-height:9rem; }
.perm-cell { text-align:center; }
.perm-cell input[type=checkbox] { margin:0; }
.perm-group > td { background:var(--panel-2); font-weight:600; font-size:.82rem;
  color:var(--text); position:sticky; left:0; }
.perm-grid tbody tr:hover .perm-role { background:var(--panel-2); }
