/* Traffic dashboard.
   One stylesheet, no framework, no build step. Motion is deliberate and short:
   150-220ms, ease-out, and always tied to something the user just did. Anything
   that animates on its own becomes noise on a screen people leave open. */

:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel-2: #1c2029;
  --line: #262b36;
  --line-soft: #1d222c;
  --text: #e6e8eb;
  --muted: #8b93a1;
  --dim: #6d7480;
  --blue: #4a7dff;
  --blue-soft: #1e2a44;
  --green: #45c286;
  --amber: #e0a33a;
  --red: #ff6b6b;
  --radius: 10px;
  --ease: cubic-bezier(.2, .7, .3, 1);
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

* { box-sizing: border-box; }

body {
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", sans-serif;
  background: var(--bg); color: var(--text); margin: 0;
  -webkit-font-smoothing: antialiased;
}

/* ---------------------------------------------------------------- chrome */
header {
  display: flex; gap: 1.5rem; align-items: center;
  padding: .85rem 1.25rem; border-bottom: 1px solid var(--line);
  background: rgba(20, 24, 33, .85); backdrop-filter: blur(10px);
  position: sticky; top: 0; z-index: 20;
}
header h1 { font-size: .95rem; margin: 0; letter-spacing: .12em; color: var(--muted); }
header nav { display: flex; gap: 1.25rem; }
header a {
  color: var(--muted); text-decoration: none; font-size: .9rem;
  padding: .2rem 0; border-bottom: 2px solid transparent;
  transition: color .15s var(--ease), border-color .15s var(--ease);
}
header a:hover { color: var(--text); }
header a.on { color: var(--text); border-bottom-color: var(--blue); }
header .who { margin-left: auto; color: var(--dim); font-size: .85rem; }

main { padding: 1.25rem; max-width: 1600px; margin: .5rem auto 0; }

h2 {
  font-size: .78rem; color: var(--muted); font-weight: 600;
  margin: 1.75rem 0 .75rem; text-transform: uppercase; letter-spacing: .09em;
}
h2:first-child { margin-top: 0; }

.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 1.1rem; margin-bottom: .8rem;
  animation: rise .22s var(--ease) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(6px); } }
.card.fyi:nth-child(2) { animation-delay: .03s; }
.card.fyi:nth-child(3) { animation-delay: .06s; }
.card.fyi:nth-child(4) { animation-delay: .09s; }

.muted { color: var(--dim); }
.small { font-size: .82rem; }

/* --------------------------------------------------------------- the ask */
.ask-head { font-size: 1.15rem; font-weight: 600; line-height: 1.3; margin: 0 0 .3rem; }
.ask-head .client { color: var(--muted); font-weight: 500; }
.ask-summary { color: #c8ced8; font-size: .92rem; margin: 0 0 .75rem; }

.meta { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; margin-bottom: .85rem; }
.pill {
  display: inline-block; padding: .12rem .55rem; border-radius: 99px;
  font-size: .72rem; letter-spacing: .04em; border: 1px solid var(--line);
  color: var(--muted); white-space: nowrap;
}
.pill.role { border-color: var(--blue); color: #9dbaff; }
.pill.ask { border-color: var(--blue); color: #9dbaff; }
.pill.delivery { border-color: #2f6f4f; color: #7ddba4; }
.pill.context { border-color: #5c5136; color: #e0c27a; }
.pill.noise { border-color: #3a3f4b; color: #7a828f; }
.pill.urgent { border-color: #7a3a3a; color: #ff9a9a; }
.pill.late { border-color: var(--red); color: var(--red); }

/* -------------------------------------------------------------- controls */
.row { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; }
button, .btn {
  font: inherit; font-size: .88rem; border-radius: 8px;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  padding: .4rem .85rem; cursor: pointer; text-decoration: none;
  transition: background .15s var(--ease), border-color .15s var(--ease),
              transform .1s var(--ease);
}
button:hover, .btn:hover { background: #232936; border-color: #333b4a; }
button:active, .btn:active { transform: translateY(1px); }
button.ok { border-color: #2c6b4a; color: #7ddba4; }
button.ok:hover { background: #16301f; }
button.no { border-color: #6b3030; color: #ff9a9a; }
button.no:hover { background: #2e1717; }
button.ghost { background: transparent; border-color: transparent; color: var(--muted); padding: .4rem .5rem; }
button.ghost:hover { color: var(--text); background: var(--panel-2); }
button[aria-expanded="true"] .chev { transform: rotate(90deg); }
.chev { display: inline-block; transition: transform .18s var(--ease); }

input[type=number], select {
  font: inherit; font-size: .88rem; background: var(--bg); color: var(--text);
  border: 1px solid var(--line); border-radius: 8px; padding: .38rem .5rem;
  transition: border-color .15s var(--ease);
}
input[type=number]:focus, select:focus { outline: none; border-color: var(--blue); }
a.link { color: #9dbaff; text-decoration: none; }
a.link:hover { text-decoration: underline; }

/* --------------------------------------------------- expanding disclosure */
.reveal { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .24s var(--ease); }
.reveal.open { grid-template-rows: 1fr; }
.reveal > div { overflow: hidden; }
.raw {
  white-space: pre-wrap; font: inherit; font-size: .85rem; color: #c2c9d4;
  background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 8px; padding: .8rem; margin: .6rem 0 0; max-height: 26rem; overflow: auto;
}

/* ------------------------------------------------------------- timelines */
.tl { margin-top: .5rem; }
.tl-person {
  display: flex; align-items: baseline; gap: .5rem; margin: .7rem 0 .35rem;
  font-size: .85rem;
}
.tl-person .nm { font-weight: 600; }
.tl-person.bare { margin: 0 0 .3rem; }
/* No header, so no gap where one would have been. */
.tl.bare { margin-top: .2rem; }
.tl-days { display: grid; gap: .3rem; grid-auto-flow: column; grid-auto-columns: 1fr; }
.tl-day {
  background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: 7px; padding: .35rem .4rem; min-height: 4.5rem;
  display: flex; flex-direction: column;
  transition: border-color .15s var(--ease), background .15s var(--ease);
}
.tl-day.drop { border-color: var(--blue); background: var(--blue-soft); }
.tl-day .dh { font-size: .68rem; color: var(--dim); margin-bottom: .3rem; white-space: nowrap; }
.tl-day .dh b { color: var(--muted); font-weight: 600; }
.tl-day.weekend { opacity: .45; }

.job {
  background: #22293a; border-left: 3px solid var(--jc, var(--blue)); border-radius: 5px;
  padding: .25rem .4rem; margin-bottom: .25rem; font-size: .74rem; line-height: 1.25;
  cursor: grab; transition: transform .14s var(--ease), box-shadow .14s var(--ease),
                            opacity .14s var(--ease);
}
.job:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,.35); }
.job.dragging { opacity: .35; cursor: grabbing; }
/* Late does NOT take the left edge - that edge is the client, and repainting it
   red made an MSF job look like a DBS one. It underlines the block instead, and
   the word "late" in the corner is red. */
/* No underline: the block already says "late" in red next to its due date, and
   the card says LATE over the date chip. Three markers for one fact is noise. */
.job.late .lt { color: #ff9a9a; font-weight: 600; }
/* The job being decided. Same colour and name as any other block - it is the
   same job - but dashed, because it is not booked yet. */
.job.preview { outline: 1px dashed var(--jc, var(--blue)); outline-offset: -2px;
               background: #262e42; }
.job .jh { color: var(--dim); font-size: .68rem; }

.bar { height: 4px; background: #2a3140; border-radius: 3px; overflow: hidden; margin-top: .3rem; }
.bar i { display: block; height: 100%; background: var(--blue); width: 0;
         transition: width .35s var(--ease), background .2s var(--ease); }
.bar i.over { background: var(--amber); }
.bar i.full { background: var(--red); }

/* Only today needs the extra contrast: its cell is lighter than the rest, so
   the standard track disappears into it. */
.tl-day.today .bar { background: #171d29; }
/* The slot absorbs the difference so every gauge in a row sits at the same
   height - comparing days is the point, and a gauge that floats up and down
   with the number of blocks above it cannot be compared at a glance. */
.tl-day .slot { flex: 1 1 auto; }
.pct { font-size: .66rem; color: var(--dim); margin-top: .15rem; }

/* --------------------------------------------------------------- toolbar */
.toolbar { display: flex; gap: .5rem; align-items: center; margin-bottom: .8rem; flex-wrap: wrap; }
.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.seg button { border: 0; border-radius: 0; background: transparent; padding: .35rem .8rem; }
.seg button.on { background: var(--blue-soft); color: #bcd0ff; }

.spinner {
  width: 14px; height: 14px; border: 2px solid var(--line);
  border-top-color: var(--blue); border-radius: 50%;
  display: inline-block; animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.toast {
  position: fixed; bottom: 1.25rem; left: 50%; transform: translateX(-50%) translateY(1rem);
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  padding: .55rem 1rem; font-size: .86rem; opacity: 0; pointer-events: none;
  transition: opacity .2s var(--ease), transform .2s var(--ease); z-index: 50;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ------------------------------------------------------------ queue grid */
.grid-wrap { overflow-x: auto; padding-bottom: .5rem; }
.qrow {
  display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: .6rem;
  align-items: start; padding: .6rem 0; border-bottom: 1px solid var(--line-soft);
}
/* Grid items default to min-width:auto, so a long block name pushed the row
   wider than its column and put the whole page on a horizontal scrollbar. */
.qrow > * { min-width: 0; }
.tl, .tl-days, .month, .tl-lanes { min-width: 0; }
.tl-day, .mcell { min-width: 0; }
.qrow:last-child { border-bottom: 0; }
.qperson .nm { font-weight: 600; font-size: .9rem; }
.qperson .sub { color: var(--dim); font-size: .74rem; }
.qperson .tot { color: var(--muted); font-size: .74rem; margin-top: .2rem; }

@media (max-width: 720px) {
  main { padding: .8rem; }
  .qrow { grid-template-columns: 1fr; }
  .tl-days { grid-auto-flow: row; grid-auto-columns: auto; }
  header { gap: .9rem; } header nav { gap: .9rem; }
}

/* -------------------------------------------------------- two-panel triage
   Actionables on the left, things to read on the right. The split is the point:
   a traffic person should see at a glance what needs a decision and what does
   not, without reading either. */
.split { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 1.5rem; }
.split > section { min-width: 0; }
.card.fyi { padding: .7rem .9rem; background: var(--panel-2); }
.card.fyi .fyi-title { font-size: .86rem; font-weight: 600; line-height: 1.3; }
.card.fyi .ask-summary { color: var(--muted); }
@media (max-width: 1100px) { .split { grid-template-columns: 1fr; } }

/* ------------------------------------------------- lifting a block by hand
   The native drag API paints its own translucent ghost that cannot be styled.
   These rules are for the real element being moved instead: it lifts, follows
   the pointer, and a placeholder holds the gap it will drop into. */
.job { display: flex; justify-content: space-between; gap: .4rem; align-items: baseline;
       touch-action: none; user-select: none; }
.job .jt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.job .jh { color: var(--dim); font-size: .68rem; white-space: nowrap; }
.job.lifting {
  position: fixed; z-index: 999; pointer-events: none; margin: 0;
  transform: rotate(-1.2deg) scale(1.03);
  box-shadow: 0 12px 28px rgba(0,0,0,.5); cursor: grabbing;
}
.job.placeholder { background: #1a2030; border: 1px dashed #39435a; border-left: 3px solid transparent; }
.job.landed { animation: settle .28s var(--ease); }
@keyframes settle { from { transform: scale(1.04); } to { transform: scale(1); } }
body.dragging-now { cursor: grabbing; }
body.dragging-now .tl-day { transition: background .12s var(--ease); }
.slot { min-height: 1.6rem; }

/* Days keep a workable width. The SCROLLING belongs to .tl-viewport, not here:
   an overflow on the strip itself puts a scrollbar under every row the moment
   the slide adds its sixth column, and it would clip the incoming column too. */
.tl-days { grid-auto-columns: minmax(9.5rem, 1fr); }

/* ------------------------------------------------------------- team tabs */
.tabs { display: inline-flex; gap: .25rem; border: 1px solid var(--line);
        border-radius: 9px; padding: .2rem; background: var(--panel); }
.tabs button { border: 0; background: transparent; padding: .32rem .8rem; border-radius: 7px;
               font-size: .85rem; color: var(--muted); }
.tabs button.on { background: var(--blue-soft); color: #bcd0ff; }
.qrow.hidden { display: none; }
.qperson .lead { font-size: .64rem; letter-spacing: .06em; color: #9dbaff;
                 border: 1px solid var(--blue); border-radius: 99px; padding: 0 .4rem;
                 margin-left: .35rem; vertical-align: middle; }

/* --------------------------------------------------------------- wordmark */
.brand { display: flex; align-items: center; gap: .5rem; }
.brand svg { display: block; }
.brand .word { font-size: 1.05rem; font-weight: 600; letter-spacing: .01em; color: var(--text); }

/* ------------------------------------------------------------- wordmark */
.brand { display: flex; align-items: center; gap: .55rem; text-decoration: none; }
.brand .mark { display: block; flex: none; }
.brand .word {
  font-size: 1.12rem; font-weight: 800; letter-spacing: .18em;
  color: var(--text); text-transform: uppercase;
  font-family: "Segoe UI Semibold", system-ui, sans-serif;
}
.brand:hover .mark { filter: brightness(1.15); }
.brand .mark, .brand .word { transition: filter .18s var(--ease), color .18s var(--ease); }

/* ------------------------------------------------------- unread summary */
.spacer { flex: 1 1 auto; }
.summary { display: flex; align-items: center; gap: .6rem; margin-bottom: 1.1rem; flex-wrap: wrap; }
.chip {
  display: inline-flex; align-items: center; gap: .5rem;
  border: 1px solid var(--line); border-radius: 99px; padding: .4rem .95rem;
  font-size: .88rem; background: var(--panel);
}
.chip .n { font-weight: 700; font-size: 1.05rem; letter-spacing: 0; }
.chip.act { border-color: #3b5bdb; color: #bcd0ff; }
.chip.act .n { color: #9dbaff; }
.chip.inf { border-color: #4a4636; color: #e0c27a; }
.chip.inf .n { color: #e0c27a; }
.chip.done { opacity: .5; border-color: var(--line); color: var(--dim); }
.chip.done .n { color: var(--dim); }

/* Unread carries a marker; read fades back. This applies to the reading pile
   only. Everything in To assign is waiting on a decision, so there is no read
   state there at all - every card carries the client wash, because that is the
   client's identity rather than a state.

   The marker must NOT be the left border: that edge is the client's colour, and
   painting it blue for unread made every card look like the same client. So
   unread thickens that edge and washes the client's own colour a little way in -
   louder, still the right colour. */
.card.unread, .card.ask-card {
  border-left-width: 4px;
  /* Faint, and finished before the text starts - the card's padding is 1.1rem,
     so the wash fades out inside that margin and never sits behind a word. A
     background-image rather than an ::after, which would paint above the text
     rather than under it. */
  background-image: linear-gradient(90deg,
    color-mix(in srgb, var(--client, var(--blue)) 9%, transparent), transparent 1.05rem);
}
/* Read means dealt with: it drops back to grey and stops competing with the
   things that still need you. It does not disappear - an actionable still needs
   a decision after it has been read - and hovering brings it back. */
.card.fyi.read { opacity: .5; filter: grayscale(.65); }
.card.fyi.read .ask-summary, .card.fyi.read .fyi-title { color: var(--dim); }

/* The whole card is the read button - clicking anywhere on an unread one marks
   it. The hover lift is the only thing saying so, so it has to be visible
   without being loud. Read cards do not respond: they are done, and coming back
   to life under the cursor made a settled pile look busy. */
.card.fyi.unread { cursor: pointer; }
/* NOT border-color: that shorthand repaints all four sides, and the left one is
   the client's colour - hovering a card wiped the very thing it is coded by.
   Three sides, and the lift comes from the shadow. */
.card.fyi.unread:hover {
  border-top-color: #39435a; border-right-color: #39435a; border-bottom-color: #39435a;
  box-shadow: 0 0 0 1px #ffffff0d, 0 6px 18px #0000004d;
}

/* The read toggle: a tick in a ring, and nothing else. Unread it is an outline
   waiting to be filled; read it is solid. No border, no label, no colour - the
   card being grey already says it has been dealt with. */
.tick {
  border: none; background: none; padding: .25rem; line-height: 0; cursor: pointer;
  color: #454d5e; border-radius: 50%;
  transition: color .15s var(--ease), transform .12s var(--ease);
}
.tick:hover { color: var(--muted); transform: scale(1.12); }
.tick svg path { opacity: 0; transition: opacity .15s var(--ease); }
.tick:hover svg path { opacity: .55; }
.tick[aria-pressed="true"] { color: var(--muted); }
.tick[aria-pressed="true"] svg path { opacity: 1; }
.tick[aria-pressed="true"] svg circle { opacity: .45; }

/* A decided card, collapsed to its record. */
.card.settled { display: flex; align-items: center; gap: .6rem; padding: .6rem .9rem; }
.settle-note { font-size: .88rem; font-weight: 600; }
.settle-note.good { color: var(--green); }
.settle-note.bad { color: #ff9a9a; }

/* When the brief landed on Monday. Next to the team, because "urgent" means
   something different on a message from an hour ago than one from last week. */
.arrived { color: var(--dim); font-size: .76rem; font-variant-numeric: tabular-nums; }

/* The classifier's confidence, as one letter. Instrumentation while we find
   out whether "high" is worth trusting - not something to decide on. */
.conf {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.15rem; height: 1.15rem; border-radius: 4px; flex: none;
  font-size: .66rem; font-weight: 700; letter-spacing: 0;
  background: #262b36; color: var(--dim);
}
.conf.high   { color: #7fd8a4; }
.conf.medium { color: #e0c27a; }
.conf.low    { color: #ff9a9a; }

/* The outcome, on the history page. */
.pill.decision { font-size: .66rem; letter-spacing: .04em; }
.pill.decision.approve { border-color: #2f6b4a; color: #7fd8a4; }
.pill.decision.reject  { border-color: #7a3b3b; color: #ff9a9a; }
.pill.decision.undone  { opacity: .55; text-decoration: line-through; }

/* Assign-all books real work, so it reads as an action rather than a link -
   but it is still a ghost button: it should not out-shout the per-card
   Approve, which is the one anybody should be reaching for by default. */
.ok-ghost { border-color: #2f6b4a; color: #7fd8a4; }
.ok-ghost:hover { background: #1d3328; }
.card { transition: opacity .2s var(--ease), border-color .2s var(--ease); }

/* --------------------------------------------------------- block contents */
.job .jt { display: flex; flex-direction: column; gap: .05rem; min-width: 0; }
.job .jt b { font-weight: 600; font-size: .73rem; overflow: hidden;
             text-overflow: ellipsis; white-space: nowrap; }
.job .jt .task { color: var(--muted); font-size: .69rem; overflow: hidden;
                 text-overflow: ellipsis; white-space: nowrap; }
.job { border-left-width: 4px; }
.tl-day.weekend { background: #14171f; }
.tl-day.weekend .dh { opacity: .55; }
button[data-weekends].on { border-color: var(--blue); color: #bcd0ff; background: var(--blue-soft); }

/* ======================================================= header, restyled */
header nav { display: flex; gap: 1.4rem; }
.inbox-link {
  display: inline-flex; align-items: center; gap: .45rem;
  color: var(--muted); text-decoration: none; font-size: .9rem;
  border-bottom: 2px solid transparent; padding: .2rem 0;
}
.inbox-link.on { color: var(--text); border-bottom-color: var(--blue); }
.badge {
  min-width: 1.35rem; text-align: center; border-radius: 99px;
  background: #232936; color: var(--dim); font-size: .74rem; font-weight: 700;
  padding: .1rem .4rem; transition: background .2s var(--ease), color .2s var(--ease);
}
.badge.live { background: var(--blue); color: #fff; }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; overflow: hidden; flex: none;
  display: grid; place-items: center; background: var(--panel-2);
  border: 1px solid var(--line); text-decoration: none;
  transition: border-color .18s var(--ease), transform .18s var(--ease);
}
.avatar:hover { border-color: var(--blue); transform: scale(1.06); }
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.avatar .initials { font-size: .72rem; font-weight: 700; color: var(--muted); letter-spacing: .02em; }

/* ============================================== roles, clients, due dates */
.role-tag {
  display: inline-block; padding: .18rem .6rem; border-radius: 6px;
  font-size: .68rem; font-weight: 700; letter-spacing: .09em;
}
.role-tag.production { background: #16341f; color: #6fd89b; border: 1px solid #2c6b4a; }
.role-tag.content    { background: #33290f; color: #e8c65c; border: 1px solid #6b5a22; }
.role-tag.design     { background: #37200f; color: #e08a4d; border: 1px solid #7a4a24; }
.role-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: .4rem; }
.role-dot.production { background: #6fd89b; } .role-dot.content { background: #e8c65c; }
.role-dot.design { background: #e08a4d; }
.tabs button.role-production.on { background: #16341f; color: #6fd89b; }
.tabs button.role-content.on    { background: #33290f; color: #e8c65c; }
.tabs button.role-design.on     { background: #37200f; color: #e08a4d; }

/* The client's colour runs down the edge of its card, unread or not. A card
   with no client falls back to a neutral rule rather than to nothing, so the
   cards still line up. `position: relative` anchors the unread dot above. */
.card { border-left: 3px solid var(--client, #39415a); }

/* A tear-off calendar page. The red band is the convention every calendar icon
   uses, so the shape is recognised before the number is read. "DUE" sits above
   it because a bare date on a brief could just as easily be the date it landed. */
.datewrap { display: inline-flex; flex-direction: column; align-items: center;
            gap: .18rem; flex: none; }
.datelabel { font-size: .56rem; font-weight: 500; letter-spacing: .16em;
             color: var(--dim); line-height: 1; }
.datechip {
  display: inline-flex; flex-direction: column; align-items: center; line-height: 1;
  border: 1px solid var(--line); border-radius: 8px; overflow: hidden;
  min-width: 3.3rem; background: var(--panel-2); flex: none;
  box-shadow: 0 1px 3px #0006;
}
.datechip .m { font-size: .64rem; font-weight: 800; letter-spacing: .12em;
               text-transform: uppercase; background: #d93a3a; color: #fff;
               width: 100%; text-align: center; padding: .2rem 0; }
.datechip .d { font-size: 1.35rem; font-weight: 700; padding: .22rem 0 .26rem;
               color: var(--text); }

/* Overdue keeps the red band - it is the calendar, not the alarm - and shouts
   with the label and a ring instead, so the two states stay distinguishable. */
.datewrap.overdue .datelabel { color: #ff8f8f; font-weight: 600; }
.datewrap.overdue .datechip { border-color: var(--red);
                              box-shadow: 0 0 0 2px #d93a3a44, 0 1px 3px #0006; }
.datewrap.overdue .datechip .m { background: #b02a2a; }
.datewrap.overdue .datechip .d { color: #ff9a9a; }

/* ------------------------------------------------------------- ask cards */
.ask-top { display: flex; align-items: center; gap: .5rem; margin-bottom: .55rem; }
.ask-card .ask-head { font-size: 1.05rem; margin-bottom: .2rem; }
.ask-card .ask-summary { color: var(--muted); font-size: .88rem; margin-bottom: .7rem; }
/* The name and the reason it is them are one sentence, so they sit as one line
   with a single space between - not two flex items a gap apart. */
.assignee { margin-bottom: .3rem; line-height: 1.4; }
.assignee .who { font-weight: 600; font-size: .98rem; }
.assignee .why { color: var(--dim); font-size: .82rem; margin-left: .5rem; }

.decide { display: flex; align-items: center; gap: .45rem; margin-top: .9rem; flex-wrap: wrap; }

/* One control, not a number box with a stray letter floating beside it. The
   native spinners are tiny and appear on hover only; these are always there and
   big enough to hit, which matters because half an hour up or down is the
   commonest correction anyone makes. */
.hours {
  display: inline-flex; align-items: stretch; border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel-2); overflow: hidden;
}
.hours input[type=number] {
  width: 2.9rem; border: none; background: none; border-radius: 0;
  text-align: right; padding: .38rem .1rem .38rem .4rem;
  font-weight: 600; font-variant-numeric: tabular-nums;
  -moz-appearance: textfield;
}
.hours input[type=number]::-webkit-outer-spin-button,
.hours input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.hours input:focus { outline: none; }
.hours:focus-within { border-color: var(--blue); }
.hours .unit { display: flex; align-items: center; color: var(--dim);
               font-size: .82rem; padding-right: .45rem; }
.hstep {
  border: none; background: none; color: var(--muted); cursor: pointer;
  padding: 0 .55rem; font-size: .95rem; line-height: 1;
  transition: background .12s var(--ease), color .12s var(--ease);
}
.hstep:hover { background: #2a3140; color: var(--text); }
.hstep:first-child { border-right: 1px solid var(--line); }
.hstep:last-child { border-left: 1px solid var(--line); }
.monday {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .35rem .5rem; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel-2); transition: border-color .15s var(--ease);
}
.monday:hover { border-color: #555f73; }
.fyi-top { display: flex; align-items: center; gap: .5rem; margin-bottom: .35rem; }

/* ============================================================== widgets */
.dash-head { display: flex; align-items: baseline; gap: .7rem; margin-bottom: 1rem; flex-wrap: wrap; }
/* Twelve columns, and every widget declares how many it wants. auto-fit left a
   ragged edge whenever the widget count was not a multiple of the column count
   - the thing that looks like a bug even though nothing is wrong. dense packing
   backfills any hole a wide tile leaves behind, so no cell is ever empty. */
.widgets {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  grid-auto-flow: row dense; gap: .9rem;
}
.widget { grid-column: span var(--span, 4); min-width: 0; }
/* Below the point where a twelfth is meaningful, halve the grid, then stack. */
@media (max-width: 1100px) {
  .widgets { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .widget { grid-column: span min(var(--span, 4), 6); }
}
@media (max-width: 720px) {
  .widgets { grid-template-columns: minmax(0, 1fr); }
  .widget { grid-column: 1 / -1; }
}
.widget {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .9rem 1rem 1rem; animation: rise .22s var(--ease) both;
}

.widget.dragging { opacity: .5; }
.widget.over { border-color: var(--blue); }
.wh { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .7rem; }
.wh h3 { margin: 0; font-size: .78rem; text-transform: uppercase; letter-spacing: .08em;
         color: var(--muted); font-weight: 600; }
.grip { cursor: grab; color: #3d4553; font-size: .8rem; user-select: none; }
.grip:active { cursor: grabbing; }

.stats { display: flex; gap: 1.4rem; flex-wrap: wrap; }
.stat { display: flex; flex-direction: column; }
.stat .n { font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
.stat .l { font-size: .74rem; color: var(--dim); }
.stat.hot .n { color: #9dbaff; } .stat.bad .n { color: var(--red); }

.rows { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.rows li { display: flex; align-items: center; gap: .6rem; }
.rows .txt { flex: 1 1 auto; min-width: 0; font-size: .84rem; }
.rows.bars .cbar { flex: 0 0 6.5rem; height: 6px; background: #232936; border-radius: 3px; overflow: hidden; }
.rows.bars .cbar i { display: block; height: 100%; background: var(--blue); }
.rows.bars .cbar i.over { background: var(--amber); }

.wweek { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(8.5rem, 1fr);
         gap: .4rem; overflow-x: auto; }
.wday { background: var(--panel-2); border: 1px solid var(--line-soft); border-radius: 8px; padding: .4rem; }
.wday .dh { font-size: .68rem; color: var(--dim); margin-bottom: .35rem; }
.mini { border-left: 3px solid var(--blue); background: #22293a; border-radius: 5px;
        padding: .25rem .4rem; margin-bottom: .25rem; display: grid; gap: .05rem; }
.mini b { font-size: .72rem; }
.mini span { font-size: .68rem; color: var(--muted); }
.mini em { font-size: .66rem; color: var(--dim); font-style: normal; }
.mini.none { border-left-color: transparent; background: transparent; color: #39414f; text-align: center; }


/* ------------------------------------------------- day lane / month grid */
/* A day is a line that fills left to right. The empty part at the end is the
   information - it is how much room is left, drawn to scale. */
.tl-lanes { display: grid; gap: .5rem; }
.tl-lane .dh { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .3rem; }
.slot.lane {
  display: flex; gap: .25rem; align-items: stretch; min-height: 3rem;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: 7px; padding: .3rem;
}
.slot.lane .job { margin-bottom: 0; min-width: 0; overflow: hidden; }
.lane-free {
  border: 1px dashed var(--line); border-radius: 5px; color: var(--dim);
  font-size: .7rem; display: flex; align-items: center; justify-content: center;
  min-width: 0; overflow: hidden;
}
.tl-lane.today .slot.lane { border-color: #3b5bdb66; }

/* A month is a calendar. Twenty columns of a week view is a wider week, not an
   overview; a grid with a dot per job shows the shape of a month at a glance. */
.month { display: grid; grid-template-columns: repeat(7, 1fr); gap: .25rem; }
.mhead { font-size: .66rem; color: var(--dim); text-transform: uppercase;
         letter-spacing: .08em; padding: .15rem .3rem; }
.mcell {
  min-height: 3.4rem; border: 1px solid var(--line-soft); border-radius: 6px;
  background: var(--panel-2); padding: .25rem .3rem; display: flex;
  flex-direction: column; gap: .2rem;
}
.mcell.outside { opacity: .35; background: none; }
.mcell.weekend { background: none; border-style: dashed; }
.mcell.today { border-color: var(--blue); }
.mcell.hot { background: #2a2320; }
.mnum { font-size: .7rem; color: var(--dim); font-variant-numeric: tabular-nums; }
.mcell.today .mnum { color: #9dbaff; font-weight: 700; }
.mdots { display: flex; flex-wrap: wrap; gap: 3px; align-content: flex-start; }
.dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--jc, var(--blue));
  display: block; cursor: default;
}
.dot.done { opacity: .35; }
.dot.late { box-shadow: 0 0 0 1.5px var(--red); }
/* Past six, individual dots stop meaning anything - a count does not. */
.dotcount {
  font-size: .64rem; font-weight: 700; color: #0d1017; background: var(--jc, var(--blue));
  border-radius: 4px; padding: 0 .28rem; line-height: 1.35; cursor: default;
}

/* Finished work stays on the timeline, struck through. "What did I get done"
   is as real a question as "what is left". */
.job.done { opacity: .5; }
.job.done .pj, .job.done .due { text-decoration: line-through; }
.job .due { color: var(--dim); font-size: .68rem; }
.job .due .lt { color: #ff9a9a; font-weight: 600; }

/* The block drags; the project name is a link and behaves like one. Two
   different actions on one object, so they get two different cursors. */
.job { cursor: grab; }
.job a.pj { cursor: pointer; }
.job a.pj:hover { text-decoration: underline; color: #cfe0ff; }
.job .pj.plain { cursor: inherit; }
.job .pj { color: inherit; text-decoration: none; font-weight: 600; font-size: .73rem;
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.pj:hover { text-decoration: underline; }
/* Today, softly. Enough to find without shouting over the work in it. */
/* Today, just barely. A lighter grey and nothing else - an outline or a colour
   makes a column look picked rather than current. */
.tl-day.today { background: #222937; border-color: #2f3849; }
.tl-day.today .dh b { color: #c3ccdc; }
.tl-lane.today .dh b { color: #9dbaff; }

/* Moving the window. */
.seg.dates { position: relative; }
.seg.dates > button:not(.datebtn) {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 .6rem; height: 100%; line-height: 0; color: var(--muted);
}
.seg.dates > button:not(.datebtn):hover { color: var(--text); }

.toolbar.tight { margin-bottom: .6rem; }
.card.mine { border-color: #34405c; }


/* ------------------------------------------------------- block detail panel */
/* A block is a few words wide and always will be. Everything that does not fit,
   and every control that would otherwise fight dragging for the same pixels,
   lives here instead. */
.jobdetail {
  position: fixed; z-index: 60; width: min(22rem, calc(100vw - 1rem));
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: .8rem .9rem; box-shadow: 0 14px 40px #000000a6;
  opacity: 0; transform: translateY(-4px);
  transition: opacity .14s var(--ease), transform .14s var(--ease);
}
.jobdetail.in { opacity: 1; transform: none; }
.jd-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .35rem; }
.jd-head b { font-size: .95rem; }
.jd-client {
  font-size: .62rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--jc); border: 1px solid var(--jc); border-radius: 99px; padding: .05rem .45rem;
}
.jd-task { color: var(--muted); font-size: .84rem; margin: .2rem 0 .55rem; line-height: 1.45; }
.jd-facts { display: flex; gap: .8rem; flex-wrap: wrap; color: var(--dim); font-size: .78rem; }
.jd-facts .lt { color: #ff9a9a; }
.jd-actions { display: flex; align-items: center; gap: .5rem; margin-top: .7rem; }
.jd-actions .monday { font-size: .78rem; color: var(--muted); text-decoration: none;
                      padding: .3rem .5rem; }
.jd-actions .monday:hover { color: var(--text); }
.tickjob[aria-pressed="true"] { border-color: #2f6b4a; color: #7fd8a4; background: #1d3328; }

/* A hidden weekend leaves an invisible gap between Friday and Monday. This is
   the difference between "five days" and "the end of one week and the start of
   the next". */
.tl-day.newweek { position: relative; }
.tl-day.newweek::before {
  content: ""; position: absolute; left: -.32rem; top: -.1rem; bottom: -.1rem;
  border-left: 2px dotted #4a5468;
}
/* The clip lives here, not on the strip. A transform carries an element's own
   overflow clip with it, so a strip that clipped itself would drag the incoming
   column along instead of revealing it - and the extra column would push a
   horizontal scrollbar onto the page for the length of the animation. */
.tl-viewport { overflow: hidden; min-width: 0; padding-bottom: .2rem; }
.tl-days { position: relative; }

/* What the colours mean. Eight clients is more than anyone holds in their head. */
.legend {
  display: flex; align-items: center; gap: .55rem; flex-wrap: wrap;
  margin: .8rem 0 .2rem; font-size: .78rem;
}
.lgd { display: inline-flex; align-items: center; gap: .35rem; color: var(--muted);
       border: 1px solid var(--line); border-radius: 99px; padding: .16rem .55rem; }
.lgd i { width: .6rem; height: .6rem; border-radius: 50%; flex: none; }
.lgd b { color: var(--text); font-weight: 700; font-size: .72rem; }
.lgd.idle { opacity: .45; }

/* My week, put away. */
.card.mine.collapsed .minebody { display: none; }
.card.mine.collapsed { padding-bottom: .7rem; }

/* --------------------------------------------- hours menu, dialog, edit mode */
/* Clicking the number offers the usual values. It is still a number field, so
   anything not on the list can simply be typed. */
.hours { position: relative; }
.hmenu {
  position: absolute; z-index: 40; top: calc(100% + .3rem); left: 0;
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 2px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  padding: .25rem; box-shadow: 0 12px 30px #000000a6; min-width: 12rem;
}
.hmenu button {
  border: none; background: none; color: var(--muted); font-size: .78rem;
  padding: .3rem .1rem; border-radius: 5px; cursor: pointer;
}
.hmenu button:hover { background: #2a3140; color: var(--text); }
.hmenu button.on { background: var(--blue-soft); color: #9dbaff; font-weight: 700; }

/* Assign all books real work. It should look like something you press on
   purpose, not like a link you brushed past. */
.assignall { display: inline-flex; align-items: center; gap: .45rem;
             border-color: #2f6b4a; color: #9be7bd; }
.assignall:hover { background: #1d3328; border-color: #3f8a61; }
.assignall .n {
  background: #2f8f5e; color: #06120c; font-weight: 800; font-size: .72rem;
  border-radius: 99px; padding: .05rem .45rem; min-width: 1.2rem; text-align: center;
}

/* The add/edit form. */
.modal {
  position: fixed; inset: 0; z-index: 80; display: flex;
  align-items: center; justify-content: center; padding: 1rem;
  background: #05070ccc; opacity: 0; transition: opacity .14s var(--ease);
}
/* An author rule beats the browsers [hidden] { display: none }, so display:flex
   above left this covering the whole page at zero opacity - invisible, and
   swallowing every click on Workloads. Never set display on something that is
   toggled with the hidden attribute without saying this too. */
.modal[hidden] { display: none; }
.modal.in { opacity: 1; }
.modal-card {
  width: min(30rem, 100%); background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; padding: 1rem 1.1rem; box-shadow: 0 20px 60px #000000c4;
  transform: translateY(6px); transition: transform .16s var(--ease);
}
.modal.in .modal-card { transform: none; }
.modal-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .8rem; }
.modal label { display: block; font-size: .76rem; color: var(--dim); margin-bottom: .6rem; }
.modal label input, .modal label select {
  display: block; width: 100%; margin-top: .22rem; font-size: .88rem;
}
.modal .row2 { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.modal-actions { display: flex; align-items: center; gap: .5rem; margin-top: .9rem; }
.modal [data-dialog-delete] { color: #ff9a9a; }
.modal input:disabled { opacity: .5; }

/* Editing is a mode: the whole page says so, so nobody wonders why a click did
   something different from last time. */
[data-editmode].on { background: #2a2440; border-color: #5b4a8f; color: #c5b3ff; }
body[data-editing="1"] .job { cursor: cell; }
body[data-editing="1"] .job:hover { outline: 1px solid #7a68b8; outline-offset: -1px; }

/* ------------------------------------------------------- toolbar alignment */
/* A segmented control is a bordered box around borderless buttons; a plain
   button carries its own border. Left alone those two do not come out the same
   height, and a toolbar of controls at three heights looks broken. One height,
   set on both, and the contents centred inside it. */
:root { --ctl-h: 2.1rem; }
.toolbar .seg,
.toolbar > .btn,
.toolbar > button,
.toolbar > .tabs,
.assignall {
  height: var(--ctl-h);
  box-sizing: border-box;
  display: inline-flex; align-items: center; justify-content: center;
  padding-top: 0; padding-bottom: 0;
}
.toolbar .seg button,
.toolbar > .tabs button { height: 100%; display: inline-flex; align-items: center; }
/* The count sits inside a fixed-height button, so it must not stretch it. */
.assignall .n { line-height: 1.3; }

/* ------------------------------------------------- blocks, menu, edit mode */
/* Room for the tick without squeezing the two lines of text. */
.job {
  display: flex; align-items: center; gap: .4rem;
  padding: .34rem .3rem .34rem .4rem; min-height: 2.5rem;
}
.job .jt { flex: 1 1 auto; min-width: 0; }
/* The right-hand end of a block is one hit area. The box inside it stays small
   - a bigger one would crowd a block that is already only a few words wide -
   but the whole strip responds, so it is easy to hit without looking. */
.jmeta { display: flex; align-items: center; gap: .25rem; flex: none;
         align-self: stretch; padding: 0 .3rem 0 .4rem; border-radius: 4px;
         transition: background .14s var(--ease); }
.jmeta[data-tickzone] { cursor: pointer; }
.jmeta[data-tickzone]:hover { background: #ffffff14; }
.jmeta[data-tickzone]:hover .jobtick { color: #7fd8a4; }
.jobtick {
  border: none; background: none; padding: 0; line-height: 0;
  color: #454d5e; border-radius: 5px; display: inline-flex;
  transition: color .14s var(--ease);
}
.smartrow .jobtick { cursor: pointer; padding: .2rem; }
.smartrow .jobtick:hover { color: #7fd8a4; background: #ffffff0f; }
.jobtick svg path { opacity: 0; transition: opacity .14s var(--ease); }
.jobtick:hover svg path { opacity: .6; }
.jobtick[aria-pressed="true"] { color: #6fbf8e; }
.jobtick[aria-pressed="true"] svg path { opacity: 1; }

/* Smart: a list you scan, so it stays a list. Half the width, because a line
   of text stretched across a monitor is harder to read, not easier. */
.smart { display: grid; gap: .2rem; max-width: 34rem; align-content: start; }
.smartrow {
  display: grid; grid-template-columns: 4.2rem minmax(0, 1fr) auto; gap: .55rem;
  align-items: center; padding: .34rem .4rem .34rem .5rem;
  background: #22293a; border-left: 3px solid var(--jc, var(--blue));
  border-radius: 5px; cursor: pointer; font-size: .78rem;
  transition: background .12s var(--ease);
}
.smartrow:hover { background: #283048; }
.smartrow.open { background: #2b3450; box-shadow: inset 0 0 0 1px #4a5a8a; }
.smartrow.done { opacity: .45; }
.smartrow.done .srname { text-decoration: line-through; }
.srwhen { color: var(--dim); font-variant-numeric: tabular-nums; font-size: .72rem; }
.smartrow.behind .srwhen { color: #ff9a9a; font-weight: 600; }
.smartrow.now .srwhen { color: #9dbaff; font-weight: 700; }
.srname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.srclient { color: var(--jc, var(--blue)); margin-right: .35rem; }
.smart.empty { padding: .8rem; max-width: none; }

/* The overflow menu: options that are set once and then left alone. */
.over { position: relative; display: inline-flex; }
.burger { display: inline-flex; flex-direction: column; justify-content: center;
          gap: 3px; width: 2.1rem; padding: 0 .55rem; }
.burger span { display: block; height: 1.5px; background: var(--muted); border-radius: 2px; }
.burger[aria-expanded="true"] span { background: var(--text); }
.overmenu {
  position: absolute; z-index: 60; top: calc(100% + .35rem); right: 0;
  min-width: 12.5rem; background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: .35rem; box-shadow: 0 14px 36px #000000a6;
}
.overmenu[hidden] { display: none; }
/* Groups, so unrelated switches do not read as one list. */
.overgroup + .overgroup { border-top: 1px solid var(--line); margin-top: .3rem; padding-top: .3rem; }
.overlabel { display: block; font-size: .62rem; letter-spacing: .1em;
             text-transform: uppercase; color: var(--dim); padding: .25rem .5rem .15rem; }
.overmenu button {
  display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;
  border: none; background: none; color: var(--muted); font-size: .82rem;
  padding: .4rem .5rem; border-radius: 6px;
}
.overmenu button:hover { background: #2a3140; color: var(--text); }
.overmenu button::before {
  content: ""; width: .8rem; height: .8rem; border: 1px solid var(--dim);
  border-radius: 3px; flex: none;
}
.overmenu button[aria-pressed="true"] { color: #9dbaff; }
.overmenu button[aria-pressed="true"]::before { background: var(--blue); border-color: var(--blue); }

/* The date field opens its own picker - a second calendar icon beside it was
   two controls for one job. */
/* The date the user sees is a plain button. A native date input draws its own
   calendar glyph and there is no reliable cross-browser way to take it off -
   every attempt to hide the pseudo-element left it showing somewhere. So the
   input is not shown at all: it is clipped to a pixel, kept in the layout only
   because showPicker() refuses to open on something that is not rendered, and
   the button calls it. No native chrome, nothing to hide.

   Clipped, not display:none - showPicker() throws on an unrendered element. */
.pickeronly {
  position: absolute; left: 50%; bottom: 0; width: 1px; height: 1px;
  opacity: 0; border: 0; padding: 0; margin: 0; pointer-events: none;
}
.datebtn {
  border: none; background: none; color: var(--muted); font-size: .8rem;
  padding: 0 .7rem; height: 100%; cursor: pointer; white-space: nowrap;
  font-variant-numeric: tabular-nums;
  border-left: 1px solid var(--line); border-right: 1px solid var(--line);
}
.datebtn:hover { color: var(--text); background: #2a3140; }
.seg.dates.off { opacity: .35; pointer-events: none; }

/* Edit mode changes what a click does, so the page has to say so loudly enough
   that nobody wonders why the last click behaved differently. */
.editmode[aria-pressed="true"] {
  background: #3a2a5e; border-color: #8b6ff0; color: #ddd0ff; font-weight: 600;
}
/* The outline needs room to breathe: drawn tight against the header it reads as
   a rendering fault rather than a state. main keeps the space reserved all the
   time so turning edit mode on does not shove the page down. */
main { border-radius: 12px; transition: box-shadow .18s var(--ease); }
body[data-editing="1"] main { box-shadow: inset 0 0 0 2px #8b6ff0; }
body[data-editing="1"] main::before {
  content: "Editing - click a job to change it";
  display: block; margin: 0 0 .9rem; padding: .4rem .8rem;
  background: #3a2a5e; color: #ddd0ff; border-radius: 8px;
  font-size: .74rem; font-weight: 600; letter-spacing: .04em;
  width: fit-content;
}
.editmode { transition: background .18s var(--ease), border-color .18s var(--ease),
                        color .18s var(--ease); }
body[data-editing="1"] .job { cursor: cell; }
body[data-editing="1"] .job:hover { outline: 2px solid #8b6ff0; outline-offset: -2px; }

/* Smart, grouped so the eye lands on what is late first. */
.smartgroup + .smartgroup { margin-top: .7rem; }
.smarthead {
  display: flex; align-items: center; gap: .4rem;
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--dim); margin-bottom: .3rem; padding-left: .15rem;
}
.smarthead .n { background: #262b36; border-radius: 99px; padding: 0 .4rem;
                font-size: .66rem; letter-spacing: 0; }
.smartgroup.behind .smarthead { color: #ff9a9a; }
.smartgroup.behind .smarthead .n { background: #3a1e1e; color: #ff9a9a; }
.smartgroup.now .smarthead { color: #9dbaff; }
.smartgroup.now .smarthead .n { background: #1e2a4a; color: #9dbaff; }

/* Editing: a block's link is inert, so it should not offer itself as one. */
body[data-editing="1"] .job a.pj { cursor: cell; text-decoration: none; }
body[data-editing="1"] .job a.pj:hover { text-decoration: none; color: inherit; }

/* ------------------------------------------------------------ brief panel */
/* A row in the open pile is one line you scan. Everything that will not fit on
   that line lives here, on the right, opened by clicking the row. */
/* Beside the list, inside the card it belongs to - and positioned over the
   space the list does not use, so opening it changes nothing about the list.
   Rows resizing under the cursor was the complaint; a short row has to stay
   short whether the panel is open or not. */
/* Two columns, always. The first is capped at the list's own width so it is
   identical whether the panel is there or not - the rows must not move when one
   opens. The second is empty until something is opened, which costs nothing:
   that space was already blank. */
.smartwrap { display: grid; grid-template-columns: minmax(0, 34rem) minmax(0, 1fr);
             gap: .9rem; align-items: start; }
.smartwrap .smart { max-width: none; }
.briefpanel {
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 10px;
  padding: .9rem 1rem;
  /* Grid keeps the column, so nothing moves when this appears - it can afford
     to fade in rather than being switched on. */
  animation: bpin .18s var(--ease) both;
}
@keyframes bpin { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
.briefpanel .bp-raw { max-height: 22rem; }
.bp-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .5rem; }
.bp-client {
  font-size: .64rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
  color: var(--jc); border: 1px solid var(--jc); border-radius: 99px; padding: .08rem .5rem;
}
.bp-title { margin: 0 0 .4rem; font-size: .98rem; line-height: 1.3; }
.bp-facts { display: flex; gap: .8rem; color: var(--dim); font-size: .78rem;
            margin-bottom: .7rem; }
.bp-facts .bad { color: #ff9a9a; }
.bp-from { font-size: .8rem; margin-bottom: .4rem; }
.bp-raw {
  margin: 0 0 .6rem; padding: .55rem .6rem; background: var(--panel);
  border: 1px solid var(--line); border-radius: 7px;
  font-size: .76rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
  max-height: 18rem; overflow-y: auto; color: var(--muted);
}
.bp-link { font-size: .78rem; color: #9dbaff; text-decoration: none; }
.bp-link:hover { text-decoration: underline; }

/* Below the list when there is genuinely no room beside it. */
@media (max-width: 940px) {
  .smartwrap { grid-template-columns: minmax(0, 1fr); }
}

/* -------------------------------------------------------------- settings */
.firstrun { border-color: #3b5bdb; }
.saved { border-color: #2f6b4a; color: #7fd8a4; padding: .6rem .9rem; }
.roles { display: grid; gap: .5rem; }
.rolecard {
  display: grid; grid-template-columns: auto 1fr; gap: .1rem .7rem;
  align-items: start; padding: .7rem .85rem; cursor: pointer;
  border: 1px solid var(--line); border-radius: 9px; background: var(--panel-2);
  transition: border-color .14s var(--ease), background .14s var(--ease);
}
.rolecard:hover { border-color: #39435a; }
.rolecard.on, .rolecard:has(input:checked) { border-color: var(--blue); background: #1c2436; }
.rolecard input { grid-row: 1 / span 2; align-self: center; accent-color: var(--blue); }
.rolename { font-weight: 600; font-size: .92rem; }
.roleblurb { color: var(--dim); font-size: .8rem; grid-column: 2; }

/* A job on a maker's own inbox, once it is finished. */
.card.settled-done { opacity: .55; }
.card.settled-done .ask-head, .card.settled-done .ask-summary { text-decoration: line-through; }

/* A standing reminder that this is not your own view of Flux. */
.lensbar {
  display: flex; align-items: center; gap: .55rem; margin: 0 0 1rem;
  padding: .45rem .8rem; border-radius: 9px;
  background: #3a2a5e; border: 1px solid #6f57c4; color: #ddd0ff;
  font-size: .8rem;
}
.lensdot { width: .5rem; height: .5rem; border-radius: 50%; background: #b9a4ff;
           flex: none; box-shadow: 0 0 0 3px #b9a4ff33; }
.lensbar b { color: #fff; }
.lensbar .ghost { color: #ddd0ff; }
.lensbar .ghost:hover { background: #4a3775; color: #fff; }

/* ------------------------------------------------------------- briefing */
/* What to do today, in a sentence. Arithmetic, not intelligence - see
   briefing.js for why that matters. */
.briefing {
  display: flex; align-items: flex-start; gap: .7rem;
  padding: .8rem 1rem; border-radius: 11px; margin-bottom: 1rem;
  background: var(--panel); border: 1px solid var(--line);
}
.briefing p { margin: 0; font-size: .95rem; line-height: 1.55; }
.bq-mark { width: .55rem; height: .55rem; border-radius: 50%; flex: none; margin-top: .5rem; }
.briefing.hot  { border-color: #7a3a3a; background: #241a1c; }
.briefing.hot  .bq-mark { background: #ff6b6b; box-shadow: 0 0 0 4px #ff6b6b26; }
.briefing.warm { border-color: #6b5a33; background: #241f18; }
.briefing.warm .bq-mark { background: var(--amber); box-shadow: 0 0 0 4px #f0a83c26; }
.briefing.calm { border-color: #2f5a48; background: #16211d; }
.briefing.calm .bq-mark { background: #4fbf87; box-shadow: 0 0 0 4px #4fbf8726; }
.bq-hot  { color: #ff9a9a; font-weight: 600; }
.bq-warm { color: #f0c07a; font-weight: 600; }
.bq-cool { color: #8fd7b0; font-weight: 600; }
.bq-job  { color: var(--text); font-weight: 600; text-decoration: none;
           border-bottom: 1px solid #4a5570; }
.bq-job:hover { border-bottom-color: var(--blue); color: #cfe0ff; }

/* ------------------------------------------------ what is open (widget) */
/* Opens DOWNWARD. A dashboard is tiles; a panel sliding out to the right would
   cover the tile beside it or shove the whole grid sideways. */
.daylist { list-style: none; margin: 0; padding: 0; display: grid; gap: .25rem; }
.dayrow { border-left: 3px solid var(--jc); border-radius: 5px; background: #22293a;
          overflow: hidden; }
.dayhead {
  display: grid; grid-template-columns: 3.6rem minmax(0,1fr) auto; gap: .5rem;
  align-items: center; width: 100%; text-align: left;
  background: none; border: none; padding: .4rem .5rem; cursor: pointer;
  font-size: .78rem; color: var(--text);
}
.dayhead:hover { background: #283048; }
.dwhen { color: var(--dim); font-size: .72rem; font-variant-numeric: tabular-nums; }
.dayrow.behind .dwhen { color: #ff9a9a; font-weight: 600; }
.dayrow.now .dwhen { color: #9dbaff; font-weight: 700; }
.dname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dname b { color: var(--jc); margin-right: .3rem; }
.dchev { color: var(--dim); transition: transform .16s var(--ease); }
.dayhead[aria-expanded="true"] .dchev { transform: rotate(90deg); }
.daybody { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .2s var(--ease); }
.dayhead[aria-expanded="true"] + .daybody { grid-template-rows: 1fr; }
.daybody > div { overflow: hidden; }
.daybody > div > * { margin-left: .5rem; margin-right: .5rem; }
.dtask { color: var(--muted); font-size: .8rem; line-height: 1.5; margin: .4rem .5rem; }
.dfacts { display: flex; gap: .7rem; color: var(--dim); font-size: .74rem; margin: .3rem .5rem; }
.draw { background: var(--panel); border: 1px solid var(--line); border-radius: 6px;
        padding: .5rem .55rem; font-size: .73rem; line-height: 1.5; white-space: pre-wrap;
        max-height: 11rem; overflow-y: auto; color: var(--muted); margin: .4rem .5rem; }
.drow { display: flex; align-items: center; gap: .5rem; margin: .5rem .5rem .6rem; }
.dayrow.done .dname { text-decoration: line-through; opacity: .6; }

/* ----------------------------------------------------- settings facts */
.factlist { display: grid; gap: 0; margin: 0; }
.factrow {
  display: grid; grid-template-columns: 11rem minmax(0, 1fr); gap: 1rem;
  padding: .55rem .2rem; border-bottom: 1px solid var(--line-soft); align-items: baseline;
}
.factrow:last-child { border-bottom: 0; }
.factrow dt { color: var(--dim); font-size: .8rem; }
.factrow dd { margin: 0; font-size: .88rem; color: var(--text); }
.factrow dd.missing { color: #f0c07a; }
.factrow dd code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                   font-size: .82rem; background: var(--panel-2); padding: .1rem .4rem;
                   border-radius: 5px; border: 1px solid var(--line); }
@media (max-width: 560px) { .factrow { grid-template-columns: minmax(0,1fr); gap: .1rem; } }

/* --------------------------------------------------------- people admin */
.plist { display: grid; gap: .4rem; }
.prow {
  display: grid; grid-template-columns: 14rem minmax(0, 1fr) auto; gap: .8rem;
  align-items: center; padding: .55rem .6rem; border-radius: 8px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
}
.prow.off { opacity: .5; }
.pwho .nm { font-weight: 600; font-size: .9rem; }
.pwho .sub { display: flex; align-items: center; gap: .45rem; margin-top: .2rem; flex-wrap: wrap; }
.pboxes { display: flex; gap: .3rem; flex-wrap: wrap; }
.pbox {
  display: inline-flex; align-items: center; gap: .3rem; cursor: pointer;
  font-size: .74rem; color: var(--muted); padding: .2rem .5rem;
  border: 1px solid var(--line); border-radius: 99px; background: var(--panel);
}
.pbox:has(input:checked) { border-color: var(--blue); color: #9dbaff; background: #1c2436; }
.pbox input { accent-color: var(--blue); margin: 0; }
@media (max-width: 900px) { .prow { grid-template-columns: minmax(0,1fr); } }

/* ------------------------------------------------------------ the front door */
body.plain { display: grid; place-items: center; min-height: 100vh; margin: 0; padding: 1.2rem; }
.gate { max-width: 27rem; width: 100%; text-align: center; }
.gatemark { display: flex; align-items: center; justify-content: center; gap: .55rem;
            margin-bottom: 1.4rem; }
.gatemark .word { font-weight: 800; letter-spacing: .18em; font-size: 1rem; }
.gate h1 { font-size: 1.35rem; margin: 0 0 .5rem; }
.gsub { color: var(--muted); font-size: .92rem; line-height: 1.6; margin: 0 0 1.3rem; }
.gerr { color: #ff9a9a; background: #241a1c; border: 1px solid #7a3a3a;
        border-radius: 9px; padding: .6rem .8rem; font-size: .86rem; margin: 0 0 1rem; }
.gfoot { color: var(--dim); font-size: .8rem; margin: 1.4rem 0 0; line-height: 1.6; }
.gfoot a { color: #9dbaff; }
.gform { display: grid; gap: .7rem; text-align: left; background: var(--panel);
         border: 1px solid var(--line); border-radius: 11px; padding: 1rem; }
.gform label { font-size: .78rem; color: var(--dim); display: grid; gap: .3rem; }
.gform select { font-size: .92rem; }

/* "Sign in as someone else" - quieter than Telegram's own button above it. */
.altsignin { margin-top: 1.1rem; display: grid; justify-items: center; gap: .4rem; }
a.btn.ghostbtn { background: transparent; border-color: var(--line); color: var(--muted);
                 text-decoration: none; font-size: .88rem; padding: .5rem 1.1rem; }
a.btn.ghostbtn:hover { color: var(--text); border-color: #39435a; background: var(--panel-2); }
.gnote { color: var(--dim); font-size: .78rem; line-height: 1.5; margin: 0; max-width: 22rem; }

/* Somebody at the door, waiting to be let in. */
.card.claims { border-color: #3b5bdb; }
.claimrow {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .55rem .6rem; border-radius: 8px; background: var(--panel-2);
  border: 1px solid var(--line-soft); margin-bottom: .4rem; font-size: .86rem;
}
.claimrow:last-child { margin-bottom: 0; }

/* Who is still locked out, and the button that fixes it. */
.card.invites { border-color: #3f5a4a; }
.invitelist { display: grid; gap: .35rem; }
.inviterow {
  display: flex; align-items: center; gap: .5rem; padding: .45rem .6rem;
  border-radius: 8px; background: var(--panel-2); border: 1px solid var(--line-soft);
  font-size: .86rem;
}

/* Sign-ins, in Settings. The invite list is folded shut: it is opened when a
   new starter arrives, not read every morning. */
#access { scroll-margin-top: 1rem; }
details.invites > summary {
  cursor: pointer; list-style: none; display: flex; align-items: center; gap: .5rem;
  padding: .5rem .6rem; border-radius: 8px; background: var(--panel-2);
  border: 1px solid var(--line-soft); font-size: .88rem; font-weight: 600;
}
details.invites > summary::-webkit-details-marker { display: none; }
details.invites > summary::before {
  content: "›"; color: var(--dim); transition: transform .16s var(--ease); display: inline-block;
}
details.invites[open] > summary::before { transform: rotate(90deg); }
details.invites > summary .n {
  background: #262b36; color: var(--muted); border-radius: 99px;
  padding: 0 .45rem; font-size: .72rem; font-weight: 700;
}
details.invites > summary:hover { border-color: #39435a; }

/* Asking to join: who they are on the left, the work picker beside the buttons.
   A grid, not the claim row's wrapping flex - a GUEST tag or a long email
   pushed "No" onto a line of its own. The name block wraps inside itself. */
.claimrow.joinrow { display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto; }
.joinrow .spacer { display: none; }
@media (max-width: 700px) {
  .claimrow.joinrow { grid-template-columns: minmax(0, 1fr) auto auto; }
  .joinwho { grid-column: 1 / -1; }
}
.joinwho { min-width: 0; }
.joinwho .muted.small { margin-top: .1rem; }
.joinrow select { font-size: .84rem; padding: .3rem .45rem; }
.role-tag.warn { background: #3a2c12; color: #ffc46b; border-color: #6b5220; }

/* The Monday step on the front door - a link wearing the main button. */
.mondaybtn { display: inline-block; text-decoration: none; padding: .7rem 1.4rem;
             font-weight: 600; font-size: .95rem; }

/* Re-check open briefs - developers only. Old name struck through, new one in green. */
.rc-apply { margin-top: .9rem; gap: .8rem; align-items: center; flex-wrap: wrap; }
.rc-row { padding: .75rem 0; border-top: 1px solid var(--line-soft); }
.rc-row:first-of-type { border-top: 0; padding-top: 0; }
.rc-head { display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap; font-weight: 600; }
.rc-sum { margin: .2rem 0 .45rem; }
.rc-swap { display: flex; align-items: center; gap: .55rem; font-size: .95rem; }
.rc-was { color: var(--muted); text-decoration: line-through; }
.rc-arrow { color: var(--dim); }
.rc-now { color: #7ddba4; font-weight: 700; }
.rc-why { margin: .3rem 0 0; color: var(--muted); }
details.rc-same > summary { cursor: pointer; font-weight: 600; }
details.rc-same[open] > summary { margin-bottom: .7rem; }
