/* Helpdesk.
   Light is the default: this is a work tool people stare at all day and the
   board gets opened on a phone in daylight.

   Every colour is a token, including the message and pill shades, because the
   dark theme is a second SET of values rather than a filter over the first.
   An inverted light palette gives you glowing reds and muddy greens; these are
   picked against the dark surface instead. Anything hardcoded here is a colour
   that would have survived the theme switch unchanged and looked broken. */
:root{
  --bg:#f6f7f9; --surface:#fff; --surface-2:#f0f2f5;
  --ink:#16191d; --ink-2:#4a5058; --muted:#6b727c;
  --line:#e2e5ea; --line-2:#cfd4dc;
  --brand:#2a6df4; --brand-dark:#1f57c9;
  --brand-fill:#2a6df4; --brand-fill-hover:#1f57c9;
  --brand-soft:#e7eefd; --brand-soft-ink:#1f57c9;
  /* Priority ramp. Deliberately distinct from the column colours so a red card
     always means urgent, never "it is in the last column". */
  --urgent:#d13438; --high:#e06c00; --normal:#5a6472; --low:#9aa2ad;
  --ok:#137a43; --warn:#a16207;
  --ok-bg:#e8f6ee;   --ok-line:#b7e2c8;   --ok-ink:#0f5132;
  --warn-bg:#fdf5e3; --warn-line:#f0dca8; --warn-ink:#7a5a06;
  --err-bg:#fdeaea;  --err-line:#f3c2c2;  --err-ink:#8a1c1c;
  --danger-bg:#fff;  --danger-line:#e7b4b4;
  --shadow:0 1px 4px rgba(20,24,31,.07);
  --radius:10px;
  --font:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  color-scheme:light;
}

/* The dark values. Surfaces stay neutral and are separated by lightness rather
   than by borders alone; accents are lifted until they carry on a dark ground
   (#2a6df4 reads as near-black at these sizes, so the brand steps up to
   #5b93ff). Status hues keep their meaning: red is still urgent. */
:root[data-theme=dark]{
  --bg:#0f1216; --surface:#171b21; --surface-2:#1f242b;
  --ink:#e6e9ed; --ink-2:#aab2bd; --muted:#7d8792;
  --line:#272d35; --line-2:#39414b;
  --brand:#5b93ff; --brand-dark:#4076e0;
  --brand-fill:#2f62d4; --brand-fill-hover:#2853b8;
  --brand-soft:#1a2740; --brand-soft-ink:#8fb4ff;
  --urgent:#ff6b6e; --high:#ff9d3d; --normal:#8b95a3; --low:#6b7480;
  --ok:#3fbd7d; --warn:#e0b84a;
  --ok-bg:#12291d;   --ok-line:#1f4a33;   --ok-ink:#7fd9a6;
  --warn-bg:#2a2313; --warn-line:#4d4020; --warn-ink:#e8c66b;
  --err-bg:#2c1618;  --err-line:#55272a;  --err-ink:#ff9a9d;
  --danger-bg:transparent; --danger-line:#5c3035;
  --shadow:0 1px 4px rgba(0,0,0,.5);
  color-scheme:dark;
}

/* "System" defers to the OS. Scoped to the attribute so an explicit choice
   always wins over the OS setting rather than fighting it. */
@media (prefers-color-scheme:dark){
  :root[data-theme=system]{
    --bg:#0f1216; --surface:#171b21; --surface-2:#1f242b;
    --ink:#e6e9ed; --ink-2:#aab2bd; --muted:#7d8792;
    --line:#272d35; --line-2:#39414b;
    --brand:#5b93ff; --brand-dark:#4076e0;
  --brand-fill:#2f62d4; --brand-fill-hover:#2853b8;
    --brand-soft:#1a2740; --brand-soft-ink:#8fb4ff;
    --urgent:#ff6b6e; --high:#ff9d3d; --normal:#8b95a3; --low:#6b7480;
    --ok:#3fbd7d; --warn:#e0b84a;
    --ok-bg:#12291d;   --ok-line:#1f4a33;   --ok-ink:#7fd9a6;
    --warn-bg:#2a2313; --warn-line:#4d4020; --warn-ink:#e8c66b;
    --err-bg:#2c1618;  --err-line:#55272a;  --err-ink:#ff9a9d;
    --danger-bg:transparent; --danger-line:#5c3035;
    --shadow:0 1px 4px rgba(0,0,0,.5);
    color-scheme:dark;
  }
}
*{box-sizing:border-box} html,body{margin:0;padding:0}
body{background:var(--bg);color:var(--ink);font:400 14px/1.5 var(--font);-webkit-font-smoothing:antialiased}
a{color:var(--brand);text-decoration:none} a:hover{text-decoration:underline}

.top{display:flex;align-items:center;gap:20px;padding:0 18px;height:52px;background:var(--surface);
  border-bottom:1px solid var(--line);position:sticky;top:0;z-index:30}
.brand{font-weight:700;font-size:15px;letter-spacing:-.01em;white-space:nowrap}
.top nav{display:flex;gap:2px;flex:1;overflow-x:auto}
.top nav a{color:var(--ink-2);padding:7px 13px;border-radius:7px;font-weight:550;font-size:13.5px;white-space:nowrap}
.top nav a:hover{background:var(--surface-2);color:var(--ink);text-decoration:none}
.top nav a.on{background:var(--brand-fill);color:#fff}
.who{display:flex;align-items:center;gap:10px;font-size:13px;color:var(--ink-2);white-space:nowrap}
.who a{color:var(--muted);font-size:12.5px}
.role{font-size:10px;text-transform:uppercase;letter-spacing:.06em;font-weight:700;padding:2px 7px;
  border-radius:99px;background:var(--surface-2);color:var(--ink-2)}
.role--admin{background:var(--brand-soft);color:var(--brand-soft-ink)}
.role--client{background:var(--brand-soft);color:var(--brand-soft-ink)}
.role--viewer{background:var(--surface-2);color:var(--ink-2)}

main{max-width:1500px;margin:0 auto;padding:18px 16px 60px}
h1{font-size:20px;font-weight:700;margin:0 0 3px;letter-spacing:-.01em}
h2{font-size:15px;font-weight:650;margin:0 0 10px}
.sub{color:var(--muted);font-size:13.5px;margin:0 0 18px}
.muted{color:var(--muted)}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:18px;margin-bottom:16px}
.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.spacer{flex:1}

.msg{padding:10px 14px;border-radius:8px;margin-bottom:16px;font-size:13.5px;border:1px solid var(--line)}
.msg--ok{background:var(--ok-bg);border-color:var(--ok-line);color:var(--ok-ink)}
.msg--warn{background:var(--warn-bg);border-color:var(--warn-line);color:var(--warn-ink)}
.msg--err{background:var(--err-bg);border-color:var(--err-line);color:var(--err-ink)}

.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:7px;cursor:pointer;
  font:600 13px var(--font);border:1px solid var(--line-2);background:var(--surface);color:var(--ink)}
.btn:hover{background:var(--surface-2);text-decoration:none}
.btn--primary{background:var(--brand-fill);border-color:var(--brand-fill);color:#fff}
.btn--primary:hover{background:var(--brand-fill-hover);border-color:var(--brand-fill-hover);color:#fff}
.btn--danger{background:var(--danger-bg);border-color:var(--danger-line);color:var(--urgent)}
.btn--sm{padding:5px 10px;font-size:12px}
.btn:disabled{opacity:.5;cursor:not-allowed}
input[type=text],input[type=email],input[type=password],input[type=date],input[type=number],
input[type=search],select,textarea{
  background:var(--surface);color:var(--ink);border:1px solid var(--line-2);border-radius:7px;
  padding:8px 10px;font:400 13.5px var(--font);width:100%}
textarea{min-height:90px;resize:vertical}
input:focus,select:focus,textarea:focus{outline:2px solid var(--brand);outline-offset:-1px;border-color:var(--brand)}
label{display:block;font-size:12px;font-weight:650;color:var(--ink-2);margin-bottom:4px}
.field{margin-bottom:12px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
/* min-width:0 because a grid item's default min-width:auto means it will not
   shrink below its content, and one unbreakable string inside is then enough
   to widen the entire page. Same reason on flex rows. */
.grid2>*{min-width:0}
.row>*{min-width:0}

/* ---- kanban ---- */
.board{display:grid;grid-template-columns:repeat(4,minmax(260px,1fr));gap:12px;align-items:start}
.col{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--radius);padding:10px;min-height:120px}
.col__h{display:flex;align-items:center;justify-content:space-between;margin-bottom:9px;padding:0 2px}
.col__t{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2)}
.col__n{font-size:11.5px;color:var(--muted);background:var(--surface);border:1px solid var(--line);
  border-radius:99px;padding:1px 8px;font-weight:650}
.col.drop{outline:2px dashed var(--brand);outline-offset:-3px;background:var(--brand-soft)}

.tk{background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--normal);
  border-radius:8px;padding:10px 11px;margin-bottom:8px;cursor:grab;display:block;color:inherit}
.tk:hover{border-color:var(--line-2);text-decoration:none;box-shadow:var(--shadow)}
.tk.p-urgent{border-left-color:var(--urgent)} .tk.p-high{border-left-color:var(--high)}
.tk.p-low{border-left-color:var(--low)}
.tk.dragging{opacity:.4}
.tk__top{display:flex;gap:8px;align-items:baseline;justify-content:space-between}
.tk__num{font-size:11px;font-weight:700;color:var(--muted);font-variant-numeric:tabular-nums}
.tk__pri{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
.p-urgent .tk__pri{color:var(--urgent)} .p-high .tk__pri{color:var(--high)}
.p-normal .tk__pri{color:var(--muted)}  .p-low .tk__pri{color:var(--low)}
.tk__t{font-size:13.5px;font-weight:550;margin:4px 0 6px;line-height:1.35;
  display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.tk__f{display:flex;gap:8px;align-items:center;font-size:11.5px;color:var(--muted);flex-wrap:wrap}
.avatar{width:19px;height:19px;border-radius:50%;background:var(--brand-fill);color:#fff;font-size:9.5px;
  font-weight:700;display:inline-flex;align-items:center;justify-content:center;flex:none}
.hrs{font-variant-numeric:tabular-nums}

/* ---- tables ---- */
table{width:100%;border-collapse:collapse;font-size:13px}
th{text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);
  font-weight:700;padding:0 10px 8px;border-bottom:1px solid var(--line);white-space:nowrap}
td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
tr:last-child td{border-bottom:none}
tbody tr:hover{background:var(--surface-2)}
.n{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
.trunc{max-width:1px;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* Wrap-and-clamp, for text that needs limiting inside a cell whose width is
   not fixed. .trunc cannot do this job: its max-width:1px only ever resolves
   against a parent that already has a width, and in an auto-layout table it
   collapses the text to nothing instead of ellipsing it. */
.clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;line-height:1.35}

/* ---- triage ---- */
.thread{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);
  padding:14px 16px;margin-bottom:10px}
.thread__h{display:flex;gap:10px;align-items:baseline;justify-content:space-between;flex-wrap:wrap}
.thread__s{font-weight:650;font-size:14px}
.thread__m{color:var(--muted);font-size:12px}
.thread__b{margin:9px 0 12px;color:var(--ink-2);font-size:13px;line-height:1.55;white-space:pre-wrap;
  max-height:150px;overflow:hidden;position:relative}
.thread__b.more::after{content:"";position:absolute;left:0;right:0;bottom:0;height:42px;
  background:linear-gradient(transparent,var(--surface))}

.empty{color:var(--muted);text-align:center;padding:34px 0;font-size:13.5px}
.login{max-width:360px;margin:12vh auto}
.login .card{padding:26px}
.login h1{text-align:center;margin-bottom:4px}
.login .sub{text-align:center}
.login .btn{width:100%;justify-content:center}
.pill{display:inline-block;font-size:11px;font-weight:650;padding:2px 8px;border-radius:99px;
  background:var(--surface-2);color:var(--ink-2);border:1px solid var(--line)}

/* ---- narrow-screen menu ----
   The breakpoint is set by the WORST CASE nav, not an average one: an admin
   has seven items, which needs roughly 860px alongside the brand, the theme
   switch and the account links. Below that the strip silently scrolled and
   five of the seven were simply invisible - no cut-off item, no scrollbar on
   a touch device, no reason to suspect anything was missing. */
.menu{display:none;position:static}
.menu > summary{list-style:none;cursor:pointer}
.menu > summary::-webkit-details-marker{display:none}
.menu__btn{display:inline-flex;align-items:center;justify-content:center;
  width:40px;height:40px;border-radius:8px;color:var(--ink-2);flex:none}
.menu__btn:hover{background:var(--surface-2);color:var(--ink)}
.menu > summary:focus-visible{outline:2px solid var(--brand);outline-offset:-2px;border-radius:8px}
.menu[open] .menu__btn{background:var(--surface-2);color:var(--ink)}
.menu__panel{position:absolute;top:100%;left:0;right:0;background:var(--surface);
  border-bottom:1px solid var(--line);box-shadow:0 8px 24px rgba(0,0,0,.14);
  padding:6px;z-index:40;max-height:calc(100vh - 52px);overflow-y:auto}
/* 44px minimum, which is the smallest a finger reliably hits. */
.menu__panel a{display:block;padding:12px 14px;min-height:44px;border-radius:8px;
  color:var(--ink);font-weight:550;font-size:15px;line-height:20px}
.menu__panel a:hover{background:var(--surface-2);text-decoration:none}
.menu__panel a.on{background:var(--brand-fill);color:#fff}
.menu__panel hr{border:0;border-top:1px solid var(--line);margin:6px 8px}

@media (max-width:1100px){ .board{grid-template-columns:repeat(2,minmax(240px,1fr))} }

@media (max-width:860px){
  .menu{display:block}
  .top nav{display:none}
  .who__name,.who__out{display:none}
  .top{gap:8px;padding:0 10px}
  .brand{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
}

@media (max-width:640px){
  main{padding:12px 10px 48px}
  .board{grid-template-columns:1fr}
  .grid2{grid-template-columns:1fr}
  .hide-sm{display:none}

  /* Headers stop being nowrap. On a wide screen nowrap keeps a header tidy; at
     390px a row of unbreakable headers is what was setting the table's minimum
     width and pushing the whole page sideways. */
  th{white-space:normal}

  /* A table that still cannot fit scrolls inside its own card rather than
     dragging the page with it. Scrolling one table is a minor annoyance;
     a page that slides horizontally on every swipe is not. */
  .card>table,.card>form>table{display:block;width:100%;overflow-x:auto}

  /* No .trunc override here. An earlier attempt set max-width:100% on it,
     which cancelled the max-width:1px that MAKES the ellipsis work - cells
     then grew to their full text width and the page got wider, not narrower. */
}

/* ---- theme switch ---- */
.theme{display:inline-flex;gap:0;background:var(--surface-2);border:1px solid var(--line);
  border-radius:99px;padding:2px}
.theme__b{display:inline-flex;align-items:center;justify-content:center;width:26px;height:22px;
  padding:0;border:0;background:none;color:var(--muted);border-radius:99px;cursor:pointer}
.theme__b:hover{color:var(--ink)}
.theme__b.on{background:var(--surface);color:var(--brand);box-shadow:var(--shadow)}
.theme__b:focus-visible{outline:2px solid var(--brand);outline-offset:1px}

/* ---- pill variants ---- */
.pill--warn{background:var(--warn-bg);color:var(--warn-ink);border-color:var(--warn-line)}
.pill--ok{background:var(--ok-bg);color:var(--ok-ink);border-color:var(--ok-line)}

/* ---- overview ---- */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px;margin-bottom:16px}
.stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:14px 16px}
.stat__l{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);font-weight:700}
.stat__v{font-size:27px;font-weight:700;line-height:1.15;margin-top:3px;font-variant-numeric:tabular-nums}
.stat__v--alert{color:var(--urgent)}
.stat__s{font-size:12px;color:var(--muted);margin-top:2px}

/* Single series, so no legend is needed - the heading names it. Bars are
   flush-bottom against the axis because the value is a count from zero. */
.trend{display:flex;align-items:flex-end;gap:3px;height:92px;padding:0 1px;
  border-bottom:1px solid var(--line)}
.trend__c{flex:1;height:100%;display:flex;align-items:flex-end}
.trend__b{width:100%;background:var(--brand);border-radius:3px 3px 0 0;min-height:2px}
.trend__x{display:flex;gap:8px;font-size:11.5px;color:var(--muted);margin-top:6px}

.prio{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em}
.prio--urgent{color:var(--urgent)} .prio--high{color:var(--high)}
.prio--normal{color:var(--muted)}  .prio--low{color:var(--low)}
.is-stale{color:var(--warn);font-weight:650}

/* ---- timesheet: days and blocks ---- */
.day{margin-bottom:18px}
.day__h{display:flex;align-items:baseline;gap:10px;padding:0 2px 6px;border-bottom:1px solid var(--line);margin-bottom:8px}
.day__d{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--ink-2)}
.day__t{font-size:13px;font-weight:700;font-variant-numeric:tabular-nums}
.blk{border:1px solid var(--line);border-radius:8px;padding:10px 12px;margin-bottom:7px;background:var(--surface)}
.blk__h{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.blk__hrs{font-size:15px;font-weight:700;font-variant-numeric:tabular-nums;min-width:42px}
.blk__desc{font-size:13.5px;flex:1;min-width:180px}
.blk__items{margin:7px 0 0;padding-left:18px;font-size:12.5px;color:var(--ink-2);line-height:1.7}
.blk__edit{margin-top:8px;border-top:1px solid var(--line);padding-top:8px}
.blk__edit>summary{font-size:12px;font-weight:650;color:var(--muted);cursor:pointer;
  list-style:none;display:inline-flex;align-items:center;gap:5px;padding:2px 0}
.blk__edit>summary::-webkit-details-marker{display:none}
.blk__edit>summary::after{content:"\25be";font-size:10px}
.blk__edit[open]>summary::after{content:"\25b4"}
.blk__edit>summary:hover{color:var(--ink)}
.blk__edit>summary:focus-visible{outline:2px solid var(--brand);outline-offset:2px;border-radius:4px}

/* Scrollable ticket picker: the list can be long and the form has to stay
   reachable without hunting for the submit button. */
.picklist{max-height:180px;overflow-y:auto;border:1px solid var(--line-2);border-radius:7px;
  background:var(--surface);padding:4px}
.picklist__i{display:flex;align-items:baseline;gap:7px;padding:5px 7px;border-radius:5px;
  cursor:pointer;font-weight:400;font-size:12.5px;margin:0;min-width:0;overflow:hidden}
.picklist__i:hover{background:var(--surface-2)}
.picklist__i input{width:auto;flex:none}
.picklist__n{font-weight:700;font-size:11px;color:var(--muted);font-variant-numeric:tabular-nums;flex:none}
.picklist__t{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1}

.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px}

/* ---- conversation ---- */
.msgline{border:1px solid var(--line);border-radius:8px;padding:11px 13px;margin-bottom:9px;
  background:var(--surface);border-left:3px solid var(--line-2)}
/* Inbound and outbound must be distinguishable at a glance without relying on
   colour alone, so the direction is also written out in the header. */
.msgline--in{border-left-color:var(--line-2)}
.msgline--out{border-left-color:var(--brand);background:var(--surface-2)}
.msgline__h{display:flex;align-items:baseline;gap:9px;flex-wrap:wrap;margin-bottom:6px}
.msgline__who{font-weight:650;font-size:13px}
.msgline__dir{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);border:1px solid var(--line);border-radius:99px;padding:1px 7px}
.msgline--out .msgline__dir{color:var(--brand);border-color:var(--brand)}
.msgline__t{font-size:11.5px;color:var(--muted)}
.msgline__b{font-size:13px;line-height:1.55;white-space:pre-wrap;color:var(--ink-2);
  max-height:340px;overflow-y:auto}
.msgline__err{margin-top:9px;padding-top:8px;border-top:1px solid var(--err-line);
  font-size:12.5px;color:var(--err-ink)}
.pill--err{background:var(--err-bg);color:var(--err-ink);border-color:var(--err-line)}

/* Theme switch, before any choice has been made.
   The page is following the operating system and the server cannot see which
   way that went, so PHP marks neither button as active. Do it here instead,
   where prefers-color-scheme is actually observable - otherwise both buttons
   sit dim and the control looks broken on a first visit. */
:root[data-theme=system] .theme__b[value=light]{
  background:var(--surface);color:var(--brand);box-shadow:var(--shadow)}
@media (prefers-color-scheme:dark){
  :root[data-theme=system] .theme__b[value=light]{background:none;color:var(--muted);box-shadow:none}
  :root[data-theme=system] .theme__b[value=dark]{
    background:var(--surface);color:var(--brand);box-shadow:var(--shadow)}
}

/* ---- unread ----
   A ticket with a reply nobody has read sorts above everything, priority
   included, so it needs to look different at a glance. Colour alone will not
   do it: the dot is paired with a text label on the card and a bolder title,
   so it still reads for anyone who cannot distinguish the hue. */
.tk--unread{border-left-color:var(--brand);background:var(--brand-soft)}
.tk--unread .tk__t{font-weight:700}
.tk__dot{display:inline-block;width:7px;height:7px;border-radius:50%;
  background:var(--brand);margin-right:5px;vertical-align:middle}
.tk__new{color:var(--brand);font-weight:650}
.nav-badge{display:inline-block;min-width:17px;height:17px;padding:0 5px;margin-left:5px;
  border-radius:99px;background:var(--urgent);color:#fff;font-size:10.5px;font-weight:700;
  line-height:17px;text-align:center;vertical-align:middle}

/* ---- assistant ---- */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}
.chat{display:flex;flex-direction:column;gap:10px;min-height:180px;max-height:46vh;
  overflow-y:auto;padding:2px 2px 10px}
.chat__m{max-width:88%;padding:10px 12px;border-radius:10px;font-size:13.5px;line-height:1.55}
.chat__m--bot{align-self:flex-start;background:var(--surface-2);border:1px solid var(--line)}
.chat__m--me{align-self:flex-end;background:var(--brand-soft);border:1px solid var(--brand)}
.chat__who{font-size:10.5px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);margin-bottom:3px}
.chat__form textarea{resize:vertical}
ul.plain{margin:0;padding-left:18px;font-size:13px;line-height:1.7;color:var(--ink-2)}
ul.plain li{margin-bottom:7px}
