/* Deliberately small and hand-written. No framework, no build step, no purge
   pass, and nothing to keep in sync with a config file. */
/* Two greys, not one. --line separates: the hairline between table rows, the
   edge of a card, the rule under a heading. --edge bounds a control, and it
   has to reach three to one against whatever is behind it or the field is a
   field only to somebody who already knew it was there. The separator was
   doing both jobs and measured 1.34 to 1, which is invisible on a phone held
   at arm's length outdoors. */
/* --rail is the navigation's own surface, a step darker than a card so the rail
   reads as the edge of the room rather than as a very tall card. --hover is the
   fill a row takes when the pointer is on it, and it is deliberately not --line:
   a hover that matches the separator colour makes the row look selected.
   --surface backs the canvas, and --line-strong draws the edges on it; both were
   referenced by the canvas rules before they existed, so the canvas had a
   transparent background and its connecting lines fell back to a literal. */
/* Native form chrome -- a date input's calendar icon, its up/down spinner --
   renders in whatever palette the browser thinks the page is. Without this,
   it renders light-mode black on a background this dark, which is not a
   missing picker, only an invisible one: the click target was there the
   whole time. */
:root{color-scheme:dark;
      --bg:#0f1113;--panel:#171a1d;--line:#272c31;--edge:#6b757e;
      --fg:#e7e9ea;--muted:#9aa3ab;
      --rail:#131619;--hover:#1e2328;--surface:#101315;--line-strong:#3b4249;
      --accent:#fb923c;--accent-dim:#c2410c;--ok:#4ade80;--warn:#fbbf24;--err:#f87171}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);
     font:15px/1.55 ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
/* ---- the shell ---------------------------------------------------------- */
/* Two columns: a navigation rail that does not move, and everything else. The
   rail scrolls on its own, so a long administration group cannot push the page
   content out of reach, and the page scrolls under a top bar that stays put. */
body.app{display:grid;grid-template-columns:15.5rem 1fr;min-height:100vh}
/* min-width:0 or a wide table inside the workspace refuses to shrink and
   stretches the whole grid, pushing the rail off the screen. A grid item's
   default minimum is its content, which is almost never what a page wants. */
.workspace{display:flex;flex-direction:column;min-width:0}

.sidenav{position:sticky;top:0;height:100vh;overflow-y:auto;
         background:var(--rail);border-right:1px solid var(--line);
         padding:.9rem .6rem 2rem;display:flex;flex-direction:column;gap:.1rem}
.brand{font-weight:700;font-size:1.15rem;color:var(--fg);
       padding:.3rem .6rem 1rem;letter-spacing:-.01em}
.brand:hover{text-decoration:none}

/* A group heading, and the dot that gives it a shape before it is read. */
.navgroup{display:flex;align-items:center;gap:.45rem;margin:1.1rem 0 .3rem;
          padding:0 .6rem;color:var(--muted);font-size:.7rem;font-weight:600;
          text-transform:uppercase;letter-spacing:.09em}
.navgroup::before{content:"";width:.45rem;height:.45rem;border-radius:999px;
                  background:var(--edge);flex:none}
.navgroup[data-dot="work"]::before{background:var(--accent)}
.navgroup[data-dot="talk"]::before{background:var(--ok)}
.navgroup[data-dot="know"]::before{background:var(--warn)}
.navgroup[data-dot="plan"]::before{background:#60a5fa}

/* A row, not a link in a sentence: the whole width is the target, and the
   underline that `a:hover` puts on every other link would look like a mistake
   on something shaped like a button. */
.navitem{display:flex;align-items:center;gap:.5rem;padding:.4rem .6rem;
         border-radius:7px;color:var(--muted);font-size:.9rem;
         border-left:2px solid transparent}
.navitem:hover{background:var(--hover);color:var(--fg);text-decoration:none}
/* Where you are. Colour alone would be the only signal for somebody who cannot
   separate these two greys, so the accent bar down the left carries it too. */
.navitem.on{background:var(--hover);color:var(--fg);font-weight:600;
            border-left-color:var(--accent)}
.navitem .unread{margin-left:auto}
.sidenav .scanlink{background:var(--accent-dim);color:#fff;font-weight:600;
                   justify-content:center;margin-bottom:.2rem}
.sidenav .scanlink:hover{background:var(--accent);color:#111}
.sidenav .scanlink.on{border-left-color:transparent}

/* ---- the top bar -------------------------------------------------------- */
/* Slim, because the navigation left it. What remains is the three things that
   have to be reachable from every page: where you are, search, and the timer
   that is running whether or not anybody is looking at it. */
.topbar{display:flex;align-items:center;gap:1rem;padding:.6rem 1.5rem;
        border-bottom:1px solid var(--line);background:var(--panel);
        position:sticky;top:0;z-index:5}
.titleblock{display:flex;flex-direction:column;gap:.05rem;flex:none;
            max-width:22rem;min-width:0}
.pagetitle{font-size:1rem;font-weight:600;margin:0;
           overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* The breadcrumb: muted, small, every segment a real link -- the same
   --muted on this same topbar background is already proven readable by
   .who a few rules down, so this is not a new colour pairing to justify. */
.breadcrumb{font-size:.72rem;color:var(--muted);white-space:nowrap;
            overflow:hidden;text-overflow:ellipsis}
.breadcrumb a{color:var(--muted);text-decoration:none}
.breadcrumb a:hover{color:var(--fg);text-decoration:underline}
.breadcrumb .sep{margin:0 .3rem;color:var(--muted)}
.signout{display:flex;align-items:center;gap:.6rem;margin:0 0 0 auto}
.who{color:var(--muted);font-family:ui-monospace,monospace;font-size:.8rem}
main{width:100%;max-width:72rem;margin:0 auto;padding:1.5rem}
/* Table, board, calendar and timeline all want the room a 72rem reading
   column does not give them -- a board with five columns was squeezed into
   the same width as a paragraph. Reading pages (a single task, a document)
   keep the cap: .wide is opt-in per page, not the new default. */
main.wide{max-width:none}
/* A task or document page has two columns -- the record and its comments --
   and squeezing both into the reading cap left the comments in a strip too
   narrow for a sentence. So that page gets more room, and the comments column
   grows with it. Both are variables on purpose: they are what a later "page
   width" setting and a draggable divider will change, per person. */
:root{--doc-page-max:96rem;--doc-side-width:clamp(22rem,30%,32rem)}
main:has(> article > .taskgrid){max-width:var(--doc-page-max)}
footer{max-width:72rem;margin:auto auto 0;padding:2rem 1.5rem 1rem;width:100%;
       display:flex;gap:1rem;
       color:var(--muted);font-family:ui-monospace,monospace;font-size:.75rem}

/* Narrow. The rail stops being a column and becomes a strip that scrolls
   sideways above the page: still every destination, still one tap, and no
   drawer that needs a script to open. */
@media (max-width:60rem){
  body.app{grid-template-columns:1fr}
  .sidenav{position:static;height:auto;flex-direction:row;flex-wrap:nowrap;
           overflow-x:auto;align-items:center;gap:.35rem;
           padding:.5rem .75rem;border-right:0;border-bottom:1px solid var(--line)}
  .brand{padding:0 .6rem 0 0}
  .navitem{flex:none;border-left:0;border-bottom:2px solid transparent;
           border-radius:7px;white-space:nowrap}
  .navitem.on{border-left:0;border-bottom-color:var(--accent)}
  /* The group headings are the one thing that does not survive the strip: a
     vertical label in a horizontal scroller is a word nobody reads. The dots
     stay, as separators that keep the grouping visible. */
  .navgroup{margin:0;padding:0 .15rem;font-size:0;letter-spacing:0}
  .navgroup::before{width:.4rem;height:.4rem}
  .topbar{padding:.6rem .9rem}
  .pagetitle{max-width:none}
  main{padding:1rem .9rem}
}
h1{font-size:1.5rem;margin:0 0 1rem}
h2{font-size:1.2rem;margin:0 0 .75rem}
.card{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:1.25rem}
.peoplehelp{margin:0 0 1rem}
.peoplehelp ul{margin:.4rem 0 0;padding-left:1.2rem}
.askaccess{margin:0 0 1rem;border-color:var(--accent)}
.askaccess h3{margin-top:0}
.askpart input,.accessnode input{width:auto;min-height:0;flex:none}
.askpart{display:flex;gap:.5rem;align-items:center;justify-content:flex-start}
.askpart.indent2{padding-left:1.5rem}
.personsections{display:grid;grid-template-columns:repeat(auto-fill,minmax(22rem,1fr));gap:1rem;align-items:start}
.personsections .card h3{margin-top:0}
.personsections .wide{grid-column:1/-1}
.accesstree details{border-top:1px solid var(--line);padding:.4rem 0}
.accesstree summary{cursor:pointer}
.accessnode{display:flex;gap:.5rem;align-items:center;justify-content:flex-start;padding:.15rem 0}
.accessnode.indent1{padding-left:1.5rem}
.accessnode.indent2{padding-left:3rem}
.accessnode.covered{opacity:.7}
.settingsgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(14rem,1fr));gap:1rem}
.settingsgrid .card h3{margin-top:0}
.settingsgrid .card a{display:block;padding:.3rem 0}
.centered{max-width:26rem;margin:4rem auto;text-align:center}
.stack{display:flex;flex-direction:column;gap:.6rem;text-align:left}
.row{display:flex;gap:.75rem;align-items:center}
.spread{justify-content:space-between}
.muted{color:var(--muted)}
.small{font-size:.85rem}
.warn{color:var(--warn);font-size:.8rem;margin-top:1rem}
input,textarea,select{background:var(--bg);color:var(--fg);border:1px solid var(--edge);
                      border-radius:6px;padding:.5rem .6rem;font:inherit;width:100%}
/* Height only: dragging one wider pushed it past its column and clipped it.
   ui.js also grows it as somebody types. */
textarea{resize:vertical;max-width:100%;box-sizing:border-box}
[popover] [data-drag-handle]{cursor:move;user-select:none}
/* A date input stretched to 100% of whatever row it sits in puts its
   calendar icon wherever that row happens to end, sometimes a full field's
   width away from the date it belongs to. Its own content is a fixed
   "MM/DD/YYYY" plus the icon, so it never needed to grow -- auto sizes it to
   that content everywhere at once, rather than a width fixed by hand on
   every date input this app has, one at a time, forever needing the next
   one remembered too. */
input[type=date]{width:auto}
button{background:var(--line);color:var(--fg);border:1px solid var(--edge);
       border-radius:6px;padding:.5rem .9rem;font:inherit;cursor:pointer}
button:hover{background:#333a41}
/* White on the dim accent rather than the body colour. The body colour on that
   background measures 4.25 to 1, which is under the 4.5 the contrast test
   requires, and the button it is on is the one people press. */
button.primary{background:var(--accent-dim);color:#fff}
button.primary:hover{background:var(--accent);color:#111}
button.ghost{background:transparent;border:1px solid var(--edge)}
table.list{width:100%;border-collapse:collapse;margin-top:1rem}
table.list th{text-align:left;color:var(--muted);font-weight:500;font-size:.8rem;
              padding:.5rem;border-bottom:1px solid var(--line)}
table.list td{padding:.6rem .5rem;border-bottom:1px solid var(--line)}

/* A grouped table's own column, one <details> per group rather than a shared
   toggle -- same disclosure triangle as .orgnode, just a heavier weight
   since this is a section heading, not a rail row. Open by default: a group
   nobody has opened yet reading as "0 items" is worse than the extra length. */
.tablegroup{margin:1.25rem 0 0}
.tablegroup>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
                    gap:.4rem;font-weight:600}
.tablegroup>summary::-webkit-details-marker{display:none}
.tablegroup>summary::before{content:"\25B8";color:var(--muted);font-size:1.1em;
                            font-weight:700;display:inline-block;width:1em;
                            transition:transform .1s}
.tablegroup[open]>summary::before{transform:rotate(90deg)}
.tablegroup table.list{margin-top:.5rem}
tr.addrow td{padding-top:.75rem}
tr.addrow input[name=title]{background:transparent;border:1px dashed var(--edge)}
.tag{background:var(--line);color:var(--muted);border-radius:4px;padding:.1rem .45rem;
     font-size:.75rem;font-family:ui-monospace,monospace}
.status{border-radius:4px;padding:.1rem .45rem;font-size:.75rem}
.status.todo{background:#2a2f35;color:var(--muted)}
.status.in_progress{background:#3a2f16;color:var(--warn)}
.status.blocked{background:#3a1e1e;color:var(--err)}
.status.done{background:#16321f;color:var(--ok)}
.status.cancelled{background:#2a2f35;color:var(--muted);text-decoration:line-through}
pre.body{white-space:pre-wrap;word-wrap:break-word;background:var(--bg);
         border:1px solid var(--line);border-radius:6px;padding:1rem;margin:1rem 0}
#panel:not(:empty){margin:1rem 0}
.err{color:var(--err);font-size:.9rem;margin:.5rem 0}
ul.files{list-style:none;padding:0}ul.files li{padding:.4rem 0;border-bottom:1px solid var(--line);display:flex;gap:.75rem;align-items:center}
/* The sign-in button, which was invisible in daylight.
   It set a white foreground and NO background, so on the dark palette it read
   as white text on a dark page and on the day palette it was white text on a
   white page. The only thing that made it appear was the hover rule painting a
   background, so the single most important control on the site could be found
   by accident and not otherwise.
   It now carries its own background in both palettes, which is what a primary
   action should have done from the start. */
.button.block{display:block;text-align:center;padding:.6rem;border-radius:6px;
              background:var(--accent-dim);color:#fff;
              border:1px solid var(--accent-dim)}
.button.block:hover{text-decoration:none;background:var(--accent);color:#111}
.divider{display:flex;align-items:center;gap:.75rem;color:var(--muted);font-size:.8rem;margin:.5rem 0}
.divider::before,.divider::after{content:"";flex:1;height:1px;background:var(--line)}

/* Status filter chips. Links, not buttons, so a filtered list is a URL. */
.filters{display:flex;gap:.4rem;flex-wrap:wrap;margin:.75rem 0}
.chip{padding:.15rem .6rem;border:1px solid var(--edge);border-radius:999px;
      color:var(--muted);font-size:.85rem;text-transform:capitalize}
.chip:hover{color:var(--fg);text-decoration:none}
.chip.on{background:var(--accent-dim);border-color:var(--accent);color:#fff}

/* Priority. Only urgent and high get colour; making all four shout means none
   of them do. */
.prio{font-size:.85rem;color:var(--muted);text-transform:capitalize}
.prio.p3{color:var(--err);font-weight:600}
.prio.p2{color:var(--warn)}

/* The task field grid on a document. */
.fields{display:grid;grid-template-columns:max-content 1fr;gap:.5rem .75rem;
        align-items:center;margin:1rem 0;padding:1rem;
        border:1px solid var(--line);border-radius:8px;background:var(--panel)}
.fields .row{grid-column:1/-1;margin-top:.25rem}
.fields label{color:var(--muted);font-size:.9rem}

/* Status/Priority/Assignee/Due on the task page: still four plain selects,
   just a row of label-over-value pairs instead of four stacked full-width
   rows -- same fields glanced at constantly, not the field list the Grid
   above stays column-shaped for (a contact/company/rules form, which can
   run to far more fields than this one ever will). Two selectors' worth of
   specificity over .fields{display:grid;...} so this wins without !important. */
.fields.fields-row{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.75rem 1.5rem}
.fields.fields-row .field{display:flex;flex-direction:column;gap:.3rem;min-width:8rem}
.fields.fields-row .row{width:100%;margin-top:.25rem}
@media (max-width:52rem){.fields.fields-row{flex-direction:column}}

/* A remove button that sits inside a list row without breaking the line. */
form.inline{display:inline}
button.small{font-size:.8rem;padding:.1rem .4rem}

.result{border:1px solid var(--line);border-left:3px solid var(--ok);
        border-radius:6px;padding:.75rem 1rem;background:var(--panel)}
.problems{margin:.5rem 0 0;padding-left:1.1rem;color:var(--warn)}
.error{color:var(--err)}

/* ---- views ------------------------------------------------------------- */
/* The four layouts are the same query arranged four ways, so the tabs sit
   above the filters rather than beside them: changing the arrangement keeps
   everything below it. */
.viewtabs{display:flex;gap:.4rem;margin:.5rem 0}
.viewbar{display:flex;gap:.6rem;align-items:end;flex-wrap:wrap;margin:.5rem 0;
         padding:.6rem .75rem;border:1px solid var(--line);border-radius:8px;
         background:var(--panel)}
.viewbar label{color:var(--muted);font-size:.8rem;display:block;margin-bottom:.2rem}
.viewbar select{width:auto;min-width:9rem}
.saveview{margin:.5rem 0 1rem}
.saveview input[name=name]{max-width:18rem}
.check{display:flex;align-items:center;gap:.35rem;color:var(--muted);
       font-size:.85rem;white-space:nowrap}

/* The toolbar: one row of buttons, each opening a native popover instead of
   a picker sitting permanently open on the page. .chip already has the
   button-vs-link difference doing nothing visually; this just resets the
   handful of <button>-only defaults (border, background, font, cursor) that
   .chip alone does not touch, so a chip that happens to be a button looks
   exactly like one that is a link. */
.toolbar{margin:.5rem 0}
.toolbar>.chip{border:1px solid var(--edge);background:none;color:var(--muted);
               font:inherit;cursor:pointer}
.toolbar>.chip:hover{background:var(--hover);color:var(--fg)}
/* .toolbar>.chip above and .chip.on tie in specificity, and being later in
   the file, the toolbar rule was winning -- Show closed read as active only
   by a font-weight bump, not the filled pill every other active state in
   this app uses. Restated here so the toolbar's own chips get the same
   active look as everything else. */
.toolbar>.chip.on{background:var(--accent-dim);border-color:var(--accent);color:#fff}
[popover].popover{
  background:var(--panel);border:1px solid var(--line);border-radius:10px;
  padding:.9rem 1rem;box-shadow:0 4px 16px rgba(0,0,0,.35);
  max-width:min(90vw,22rem);color:var(--fg)}
[popover].popover label{color:var(--muted);font-size:.8rem;display:block;margin-bottom:.25rem}
[popover].popover select{width:100%}
[popover].popover .filters{margin:0}
[popover].popover form.row{margin:0}
/* The list-toolbar popovers above are sized for a handful of selects. The
   Time popover (document.html) carries labeled inputs, a timer form and a
   growing list of logged entries -- needs real width and a capped height
   with its own scrollbar rather than running off the viewport. Three
   selectors, not two, so this wins over .popover's max-width regardless of
   file order. */
[popover].popover.popover-lg{max-width:min(92vw,34rem);max-height:80vh;overflow:auto}
[popover].popover.popover-new{max-width:min(94vw,46rem);width:100%}
/* The task panel: a whole document page's worth of content (fields, Details,
   Checklist, Comments, everything), so it gets most of the viewport instead
   of the list/form-sized popover widths above. Still a popover, not a
   <dialog> -- same native show/hide, ESC, and light-dismiss, no script,
   just sized for what it is holding. */
[popover].popover.popover-xl{max-width:min(96vw,64rem);max-height:92vh;
                             width:100%;overflow:auto;padding:1.25rem 1.5rem}
.check input{width:auto}
/* A YouTube link on its own line (markdown.go), and video attachments. */
.video{position:relative;width:100%;max-width:48rem;aspect-ratio:16/9;margin:.75rem 0}
.video iframe{position:absolute;inset:0;width:100%;height:100%;border:0;border-radius:8px}
.attachvideo{display:block;width:100%;max-width:36rem;margin:.4rem 0;border-radius:8px}
/* Attachments: All/Documents/Pictures/Videos and Hide previews, read by :has(). */
.attachtools{flex-wrap:wrap;gap:.4rem;margin:.25rem 0 .5rem}
.attachtools input{width:auto;min-height:0;margin:0 .25rem 0 0}
.attachtools .chip:has(input:checked){background:var(--accent-dim);border-color:var(--accent);color:#fff}
.attachments:has(input[name=attshow][value=doc]:checked) li:not([data-kind=doc]),
.attachments:has(input[name=attshow][value=image]:checked) li:not([data-kind=image]),
.attachments:has(input[name=attshow][value=video]:checked) li:not([data-kind=video]){display:none}
.attachments:has(.atthide:checked) :is(.attachvideo,.attachpreview-link,.videopop-open){display:none}
.attachpreview{display:block;max-width:14rem;max-height:10rem;border-radius:6px;margin:.4rem 0}
.removedfiles{margin:.5rem 0}
/* The formatting toolbar, and "Open big": the same form, lifted into a large
   editor over the page (ui.js toggles .mdbig-on), so nothing is copied. */
.mdbar{display:flex;flex-wrap:wrap;gap:.25rem;margin:.25rem 0}
.mdbar button{min-height:0;padding:.2rem .55rem}
.mdbar .mdbig-toggle{margin-left:auto}
form.mdbig-on{position:fixed;inset:6vh 8vw;z-index:1000;display:flex;flex-direction:column;gap:.5rem;
  background:var(--panel);border:1px solid var(--edge);border-radius:10px;padding:1rem 1.25rem;
  box-shadow:0 12px 40px rgba(0,0,0,.55)}
form.mdbig-on textarea{flex:1;height:auto !important;min-height:0;resize:none;font-size:1rem}
/* The live preview under a box (POST /preview): what it will look like once
   saved. Only after the Preview button, never while empty; beside the text
   in Open big. */
.mdpair{display:flex;flex-direction:column;gap:.5rem}
.mdpreview{border:1px dashed var(--edge);border-radius:8px;padding:.5rem .75rem;max-height:40vh;overflow:auto}
.mdpreview:empty,form:not(.mdpreview-on) .mdpreview{display:none}
.mdbar [aria-pressed=true]{background:var(--edge)}
.mdpreview-label{margin-bottom:.25rem}
form.mdbig-on .mdpair{flex:1;flex-direction:row;min-height:0}
form.mdbig-on .mdpair textarea{flex:1 1 50%}
form.mdbig-on.mdpreview-on .mdpreview{flex:1 1 50%;max-height:none;display:block}
[popover] :is(h1,h2,h3,h4){cursor:move;user-select:none}
form.mdbig-on .mdbar{cursor:move;user-select:none}
/* The "Open larger" popup for a video: drag by the bar (ui.js data-drag-handle),
   resize from the corner, and the player fills whatever size it is given.
   display:flex only while open, so the closed popover stays hidden. */
.videopop-open{display:inline-block;margin:0 0 .75rem}
[popover].popover.videopop{max-width:none;width:min(92vw,64rem);height:min(80vh,40rem);min-width:18rem;min-height:12rem;
  padding:0;overflow:hidden;resize:both}
.videopop:popover-open{display:flex;flex-direction:column}
.videopop-bar{display:flex;justify-content:space-between;align-items:center;gap:.5rem;padding:.35rem .6rem;cursor:move;user-select:none;border-bottom:1px solid var(--line)}
.videopop-frame{flex:1;min-height:0;background:#000}
.videopop-frame iframe,.videopop-frame video{display:block;width:100%;height:100%;border:0}
/* The New form's kind picker: a document has no priority, due date or
   assignee, so those hide while Document is chosen. :has() rather than script. */
form:has(select[name=kind] option[value=doc]:checked) .taskfields{display:none}

/* A button that reads as a link -- the grouped table's title cell needs a
   click target that both opens the task panel (popovertarget, a <button>-only
   attribute) and fires the htmx fetch for it, which an <a> cannot do at
   once. Visually identical to a plain link either way. */
button.linklike{background:none;border:0;padding:0;margin:0;font:inherit;
                color:var(--accent);cursor:pointer;text-align:left}
button.linklike:hover{text-decoration:underline}

/* ---- board ------------------------------------------------------------- */
.board{display:flex;gap:.75rem;overflow-x:auto;padding-bottom:.5rem;margin-top:1rem}
.boardcol{flex:0 0 15rem;background:var(--panel);border:1px solid var(--line);
          border-radius:8px;padding:.6rem;display:flex;flex-direction:column}
.boardcol h3{font-size:.85rem;text-transform:capitalize;color:var(--muted);
             margin:0 0 .5rem;display:flex;gap:.4rem;align-items:baseline}
/* A minimum height so an empty column is still a target you can drop onto.
   Without it there is nowhere to put the first blocked job. */
.cards{display:flex;flex-direction:column;gap:.5rem;min-height:4rem;flex:1}
.cards.over{outline:1px dashed var(--accent);outline-offset:3px;border-radius:6px}
.card2{background:var(--bg);border:1px solid var(--line);border-radius:6px;
       padding:.5rem .6rem;cursor:grab}
.card2 a{display:block;margin-bottom:.35rem}
.card2 .meta{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap}
.card2.dragging{opacity:.45}
.card2.pending{opacity:.7}
.card2.refused{border-color:var(--err)}

/* ---- calendar ---------------------------------------------------------- */
.calhead{margin-top:1rem}
.calhead h3{margin:0}
table.calendar{width:100%;border-collapse:collapse;table-layout:fixed;margin-top:.5rem}
table.calendar th{color:var(--muted);font-weight:500;font-size:.75rem;
                  text-align:left;padding:.3rem .4rem}
table.calendar td{border:1px solid var(--line);vertical-align:top;height:6rem;
                  padding:.3rem;overflow:hidden}
table.calendar td.outside{background:#121417}
table.calendar td.today{border-color:var(--accent)}
.daynum{color:var(--muted);font-size:.75rem;font-family:ui-monospace,monospace}
.calitem{display:block;font-size:.78rem;margin-top:.2rem;padding:.1rem .3rem;
         border-radius:3px;background:var(--line);overflow:hidden;
         text-overflow:ellipsis;white-space:nowrap}
.calitem.done{opacity:.55;text-decoration:line-through}
.calitem.blocked{background:#3a1e1e}
.calitem.in_progress{background:#3a2f16}

/* ---- timeline ---------------------------------------------------------- */
.timeline{margin-top:1rem;display:flex;flex-direction:column;gap:.35rem}
.tlrow{display:grid;grid-template-columns:12rem 1fr;gap:.75rem;align-items:center}
.tllabel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem}
.tltrack{position:relative;height:1.1rem;background:var(--panel);
         border:1px solid var(--line);border-radius:4px}
.tlbar{position:absolute;top:2px;bottom:2px;border-radius:3px;
       background:var(--accent-dim);min-width:2px}
.tlbar:hover{background:var(--accent)}
.tlbar.done{background:#1f6b3a}
.tlbar.blocked{background:#8d3030}
.tlbar.cancelled{background:var(--line)}

/* ---- operations -------------------------------------------------------- */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(9rem,1fr));
       gap:.75rem;margin:1rem 0}
.stat{background:var(--panel);border:1px solid var(--line);border-radius:8px;
      padding:.7rem .85rem}
.stat dt{color:var(--muted);font-size:.72rem;text-transform:uppercase;
         letter-spacing:.08em;margin:0 0 .25rem}
.stat dd{margin:0;font-family:ui-monospace,monospace;font-size:1.05rem}
/* Colour only when something is wrong. A dashboard where everything is
   coloured is a dashboard where nothing stands out. */
.stat.bad{border-color:var(--err)}
.stat.bad dd{color:var(--err)}
.stat.warnish{border-color:var(--warn)}
.stat.warnish dd{color:var(--warn)}
.error-cell{max-width:28rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
code{font-family:ui-monospace,monospace;font-size:.85em}

/* ---- search ------------------------------------------------------------ */
.topsearch{margin:0}
.topsearch input{width:14rem;padding:.3rem .5rem;font-size:.85rem}
.searchbar{margin:1rem 0}
.searchbar input[name=q]{flex:1}
.searchbar select{width:auto}
ul.hits{list-style:none;padding:0;margin:1rem 0}
ul.hits li{padding:.7rem 0;border-bottom:1px solid var(--line)}
ul.hits li a{font-size:1.02rem}
.snippet{margin:.25rem 0;color:var(--muted);font-size:.9rem}
/* The engines mark their own matches. Both escape the surrounding text first,
   which is why the fragment is trusted at all. */
.snippet mark{background:var(--accent-dim);color:#fff;border-radius:2px;padding:0 .1rem}

/* ---- conversation ------------------------------------------------------ */
.channelgrid{display:grid;grid-template-columns:1fr 15rem;gap:1.5rem;margin-top:1rem}
.sidebar h3{font-size:.85rem;color:var(--muted);text-transform:uppercase;
            letter-spacing:.08em;margin:1rem 0 .4rem}
ul.plain{list-style:none;padding:0;margin:0}
ul.plain li{padding:.25rem 0;font-size:.9rem}
ul.messages{list-style:none;padding:0;margin:0}
ul.messages li{padding:.6rem 0;border-bottom:1px solid var(--line)}
ul.messages .author{font-weight:600;font-size:.9rem}
/* pre-wrap, because somebody typing a numbered list expects their line breaks
   to survive. The text is escaped by the template either way. */
ul.messages .body{margin:.2rem 0 0;white-space:pre-wrap;word-wrap:break-word}
.mentions{margin:.35rem 0 0;display:flex;gap:.3rem;flex-wrap:wrap}
.composer{margin-top:1rem}
.unread{background:var(--accent-dim);color:#fff;border-radius:999px;
        padding:.05rem .45rem;font-size:.75rem;font-family:ui-monospace,monospace}
@media (max-width:52rem){.channelgrid{grid-template-columns:1fr}}

/* ---- documents --------------------------------------------------------- */
/* Fields left, activity right -- the record on the wide column, everything
   that happened around it on the fixed narrow one, same shape as .dashgrid
   and .channelgrid rather than a new pattern. Parts and the asset-only
   sections sit below this grid, full width: they are dense multi-field forms
   that would wrap into unreadability at either column's size. */
.taskgrid{display:grid;grid-template-columns:minmax(0,1fr) var(--doc-side-width);gap:1.5rem;
          align-items:start;margin:1rem 0}
.taskside h3{margin-top:1.5rem}
.taskside h3:first-child{margin-top:0}
/* Comments stay in view on a long task: the right column sticks and scrolls on
   its own. Not on a narrow screen, where the columns stack. */
.taskside{position:sticky;top:1rem;align-self:start;max-height:calc(100vh - 2rem);overflow:auto}
@media (max-width:60rem){.taskgrid{grid-template-columns:1fr}}
@media (max-width:60rem){.taskside{position:static;max-height:none;overflow:visible}}

/* A hover explainer for a control whose purpose is not obvious from its
   label alone. Native title attribute, same mechanism as the other hover
   text in this codebase -- no script, no new dependency. */
.help{display:inline-flex;align-items:center;justify-content:center;
      width:1.1rem;height:1.1rem;border-radius:999px;border:1px solid var(--edge);
      color:var(--muted);font-size:.7rem;cursor:help;flex:none}

/* The rendered body. Generous line height because a runbook is read standing
   up, often on a phone, sometimes in gloves. */
.prose{line-height:1.7;margin:1rem 0}
.prose h1,.prose h2,.prose h3{margin:1.2rem 0 .5rem}
.prose h1{font-size:1.35rem}.prose h2{font-size:1.15rem}.prose h3{font-size:1rem}
.prose p{margin:.6rem 0}
.prose ul,.prose ol{margin:.6rem 0;padding-left:1.4rem}
.prose li{margin:.2rem 0}
.prose li.task{list-style:none;margin-left:-1.2rem}
.prose code{background:var(--panel);border:1px solid var(--line);border-radius:3px;
            padding:.05rem .25rem}
.prose pre{background:var(--panel);border:1px solid var(--line);border-radius:6px;
           padding:.8rem;overflow-x:auto}
.prose pre code{background:none;border:0;padding:0}
.prose blockquote{margin:.8rem 0;padding:.2rem 0 .2rem .9rem;
                  border-left:3px solid var(--line);color:var(--muted)}
.prose hr{border:0;border-top:1px solid var(--line);margin:1.2rem 0}
/* An embedded entity: a live card rather than a picture of one. */
a.embed{display:flex;gap:.5rem;align-items:center;padding:.5rem .7rem;margin:.7rem 0;
        background:var(--panel);border:1px solid var(--line);border-radius:6px}
a.embed:hover{border-color:var(--accent);text-decoration:none}

ul.checklist{list-style:none;padding:0;margin:.5rem 0}
/* A column now, not a single row -- the optional description disclosure
   below needs its own line, full width, rather than fighting the checkbox/
   text/Remove row for space. */
ul.checklist li{display:flex;flex-direction:column;padding:.3rem 0;
                border-bottom:1px solid var(--line)}
ul.checklist li>.row{gap:.6rem;align-items:center}
ul.checklist li.ticked .text{color:var(--muted);text-decoration:line-through}
button.tick{background:none;border:1px solid var(--edge);border-radius:4px;
            padding:.05rem .4rem;font-size:.9rem;line-height:1.2}
ul.checklist .text{flex:1}

/* The per-step description, collapsed by default -- same disclosure
   triangle as .orgnode above, just a smaller, quieter toggle since most steps
   will not have one at all. */
ul.checklist .stepnote{margin:.2rem 0 0 1.9rem}
ul.checklist .stepnote>summary{list-style:none;cursor:pointer;color:var(--muted);font-size:.78rem}
ul.checklist .stepnote>summary::-webkit-details-marker{display:none}
ul.checklist .stepnote p{margin:.3rem 0 0}

/* A diff reads as lines, because a procedure is a list of steps and a
   word-level diff of one is a wall of colour. */
.diff{font-family:ui-monospace,monospace;font-size:.82rem;background:var(--panel);
      border:1px solid var(--line);border-radius:6px;padding:.5rem;overflow-x:auto;
      margin:.5rem 0 1rem}
.dl{display:flex;gap:.5rem;white-space:pre-wrap}
.dl .ln{color:var(--muted);min-width:2.2rem;text-align:right;user-select:none}
.dl .dk{width:1ch;color:var(--muted)}
.dl.add{background:#12291a}.dl.add .dk{color:var(--ok)}
.dl.del{background:#2b1618}.dl.del .dk{color:var(--err)}

/* ---- time -------------------------------------------------------------- */
.timer{display:flex;align-items:center;gap:.5rem;margin:0;
       background:var(--accent-dim);padding:.2rem .5rem;border-radius:6px}
.timer a{color:#fff;font-size:.85rem;max-width:12rem;overflow:hidden;
         text-overflow:ellipsis;white-space:nowrap}
.timer .elapsed{font-family:ui-monospace,monospace;font-size:.8rem;color:#fff}
.timer button{padding:.15rem .5rem;font-size:.8rem}
/* A bar, because "who is over" is answered by scanning rather than reading. */
.loadbar{display:inline-block;width:7rem;height:.6rem;background:var(--line);
         border-radius:999px;overflow:hidden;vertical-align:middle}
.loadbar span{display:block;height:100%;background:var(--ok)}
.loadbar.over span{background:var(--err)}

/* ---- maintenance -------------------------------------------------------- */
/* Overdue is red wherever it appears, and it appears on a table cell, on a
   chip, and inside a list item. One rule rather than three, because a schedule
   that is late on one page and grey on another is worse than no colour. */
.over{color:var(--err)}
.row.wrap{flex-wrap:wrap}
/* The schedule list on an asset carries several small forms per row. Without
   this they stack, and a machine with four schedules becomes a page of
   buttons. */
[data-schedules] li{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
[data-schedules] form.inline{margin:0}
[data-readings] li,[data-pm-history] li{display:flex;align-items:center;gap:.5rem;
                                        flex-wrap:wrap}
[data-compliance]{gap:.5rem;margin:.5rem 0}
[data-compliance] .chip{background:var(--line);color:var(--fg)}

/* ---- the plant tree ----------------------------------------------------- */
/* Indent by padding rather than nesting, so the numbers in the columns to the
   right stay in a straight line. A tree that cannot be read across is a tree
   nobody uses to compare two machines. */
.tree{display:inline-block}
tr.down td:first-child{border-left:3px solid var(--err);padding-left:.4rem}
.tag.crit-safety{background:#3a1e1e;color:var(--err)}
.tag.crit-high{background:#3a2f16;color:var(--warn)}
[data-spaces] li,[data-children] li,[data-downtime] li{display:flex;align-items:center;
                                                       gap:.5rem;flex-wrap:wrap}
[data-space-form]{margin:.75rem 0}

/* ---- the printed label -------------------------------------------------- */
/* The label is a physical object. It has a white background and black ink
   whatever the screen is set to, because a dark-mode label prints as a solid
   black rectangle and empties the cartridge. */
.label{background:#fff;color:#000;border:1px solid #000;border-radius:4px;
       padding:.75rem;width:max-content;margin:1rem 0;text-align:center}
.label-code{font-family:ui-monospace,monospace;font-size:1.6rem;font-weight:700;
            letter-spacing:.06em}
.label-symbol svg{display:block;margin:.4rem auto}
.label-title{font-size:.9rem;max-width:16rem;margin:0 auto}
.label-url{font-family:ui-monospace,monospace;font-size:.62rem;color:#333;
           word-break:break-all;max-width:16rem;margin:.2rem auto 0}
@media print{
  .noprint,.topbar,.sidenav,footer{display:none !important}
  body{background:#fff}
  /* The grid has to go too, or the printed page keeps a 15.5rem empty column
     where the navigation used to be and every label prints half width. */
  body.app{display:block}
  main{padding:0;max-width:none}
  .label{border:0;margin:0}
}

/* ---- canvas ------------------------------------------------------------- */
/* Absolutely positioned cards over one SVG of lines. The surface scrolls in
   both directions rather than clipping, because a diagram that cannot be
   scrolled to is a diagram with a machine hidden off the edge. */
.canvas{position:relative;min-height:26rem;margin:1rem 0;overflow:auto;
        background:var(--surface);border:1px solid var(--line);border-radius:8px}
.canvas-edges{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
.canvas-edges line{stroke:var(--line-strong,#3b4249);stroke-width:2}
.node{position:absolute;background:var(--bg);border:1px solid var(--line);
      border-radius:6px;padding:.5rem;overflow:hidden;font-size:.85rem}
.node header{gap:.4rem}
.node-label{font-weight:600}
.node .node-move{margin-top:.35rem;gap:.25rem}
/* A card whose entity is gone says so rather than lying quietly, and one the
   reader was not granted keeps its place and loses its content. */
.node.gone{border-style:dashed;opacity:.6}
.node.hidden{border-style:dotted;opacity:.45}
[data-suggestions] li,[data-decided] li,[data-canvases] li{display:flex;
    align-items:center;gap:.5rem;flex-wrap:wrap}
[data-new-token] pre{white-space:pre-wrap;word-break:break-all}
.node.dragging{opacity:.85;box-shadow:0 6px 20px rgba(0,0,0,.35);z-index:10}
.node:not(.dragging){transition:none}

/* ---- the plant floor ----------------------------------------------------- */
/* Everything below here is for somebody standing at a machine holding a phone
   in one hand, and every rule in it has a reason that is about that and not
   about how it looks on a laptop. */

/* Daylight.
   A second palette, chosen with a button rather than read off the device,
   because the phone is set to dark and the person carrying it has walked
   outside. Every pair here is checked by a test that computes the contrast
   ratio, so a colour that looks fine on the screen it was picked on cannot
   ship at 3 to 1. */
:root[data-theme="day"]{
  color-scheme:light;
  --bg:#ffffff;--panel:#f1f3f5;--line:#5a6570;--fg:#101317;--muted:#41494f;
  --edge:#5a6570;
  --rail:#f7f8fa;--hover:#e6e9ec;--surface:#fafbfc;--line-strong:#8b959d;
  --accent:#8a3b00;--accent-dim:#6b2d00;--ok:#0f5c2e;--warn:#7a4c00;--err:#9b1c1c;
}
/* The controls need their own treatment in daylight: the dark palette uses the
   line colour as a button face, and on white that leaves the label at 3.2 to
   one. A panel face with a real border reads as a button and measures 17. */
/* .button as well as button, throughout. These were element selectors, so an
   anchor styled as a button -- which is what the single sign-on control on the
   login page is -- matched none of them and kept the dark palette's treatment
   on a white page. */
:root[data-theme="day"] button,
:root[data-theme="day"] .button{background:var(--panel);border:1px solid var(--edge)}
:root[data-theme="day"] button:hover,
:root[data-theme="day"] .button:hover{background:#e2e6ea}
:root[data-theme="day"] button.primary,
:root[data-theme="day"] .button.primary{background:var(--accent-dim);color:#fff;
                                       border-color:var(--accent-dim)}
:root[data-theme="day"] button.primary:hover,
:root[data-theme="day"] .button.primary:hover{background:var(--accent);color:#fff}
:root[data-theme="day"] .tag{background:var(--panel);color:var(--muted);
                             border:1px solid var(--line)}
:root[data-theme="day"] .status.todo{background:var(--panel);color:var(--muted)}
:root[data-theme="day"] .status.in_progress{background:#fdf3d8;color:#5c3a00}
:root[data-theme="day"] .status.blocked{background:#fbe3e3;color:#7f1616}
:root[data-theme="day"] .status.done{background:#dcf3e5;color:#0b4423}
:root[data-theme="day"] .status.cancelled{background:var(--panel);color:var(--muted)}
:root[data-theme="day"] .card2,:root[data-theme="day"] .node{background:#fff}
:root[data-theme="day"] table.calendar td.outside{background:#e9ecef}
:root[data-theme="day"] .dl.add{background:#dcf3e5}
:root[data-theme="day"] .dl.del{background:#fbe3e3}
:root[data-theme="day"] .snippet mark{background:#ffe08a;color:#3a2a00}
:root[data-theme="day"] .unread,:root[data-theme="day"] .chip.on{color:#fff}

/* Touch.
   Forty-eight pixels rather than the forty-four the platform guidelines say,
   because those numbers are for a bare fingertip and this is a gloved one. A
   coarse pointer is the only signal that says "a finger", so it is what this
   hangs off rather than a width: a tablet in a cradle is not a small screen
   and still cannot be tapped accurately. */
@media (pointer:coarse){
  button,input,select,textarea,.chip,.button,a.embed,button.tick{min-height:48px}
  button{padding:.75rem 1.1rem;font-size:1rem}
  button.small{font-size:.95rem;padding:.5rem .8rem;min-width:48px}
  button.tick{min-width:48px;font-size:1.1rem}
  input,select,textarea{padding:.7rem .75rem;
    /* Sixteen pixels or more, or the browser zooms the whole page when the
       field is focused and the layout jumps under the thumb. */
    font-size:16px}
  .topbar{gap:.75rem;padding:.5rem .75rem;flex-wrap:wrap}
  /* Every rail row is a target, so it gets the gloved minimum whether the rail
     is a column or the sideways strip a phone turns it into. */
  .navitem{min-height:48px}
  .sidenav{gap:.3rem}
  main{padding:1rem .75rem}
  ul.files li,ul.checklist li{padding:.7rem 0;gap:.6rem}
  .row{gap:.6rem;flex-wrap:wrap}
  .fields{grid-template-columns:1fr;gap:.35rem}
  .fields label{margin-top:.5rem}
  table.list td,table.list th{padding:.75rem .5rem}
}

/* A control sized for a glove wherever it appears, coarse pointer or not. The
   scan box and the answers to a conflict are pressed in the worst conditions
   this product has, so they do not wait to be told the pointer is a finger. */
button.big,.button.big{min-height:56px;font-size:1.05rem;padding:.9rem 1.4rem}

/* Rows of small forms wrap rather than squeeze. Every list on the outbox page
   is a row of buttons on a phone, and squeezed they are one target. */
ul.touchlist li{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
ul.touchlist form.inline{margin:0}
[data-queue] li,[data-undecided] li{border-left:3px solid var(--warn);padding-left:.6rem}
[data-recent] li{border-left:3px solid transparent;padding-left:.6rem}

/* The scanner. The camera fills the width and is capped in height, because a
   viewfinder taller than the screen puts the button that stops it out of
   reach. */
.scan{margin:1rem 0;display:flex;flex-direction:column;gap:.85rem}
.scan video{width:100%;max-height:60vh;background:#000;border-radius:8px;
            border:1px solid var(--edge)}
.scan input{font-family:ui-monospace,monospace;font-size:1.25rem;
            letter-spacing:.08em;text-transform:uppercase}
.scanlink{font-weight:600}

/* Safe areas. A phone with rounded corners and a home indicator hides the
   bottom of anything that assumes the viewport is a rectangle. */
@supports (padding:max(0px)){
  body{padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}
  footer{padding-bottom:max(1rem,env(safe-area-inset-bottom))}
}

/* ---- the working surfaces ------------------------------------------------ */
/* Everything above this line was built to be correct and read at arm's length
   in a plant room, and none of it was built to be looked at for eight hours by
   somebody working a queue. Both are real. What follows changes weight,
   spacing and state feedback only -- no colour token moves, so every contrast
   pair the test asserts still holds. */

/* A page needs a top edge that is not the same weight as a table row. */
main > section > .row.spread:first-child{margin-bottom:1rem}
h1{font-size:1.4rem;letter-spacing:-.01em;margin:0}
h1 + .muted{align-self:center}

/* Lists.
   The old table put a hairline under every row, which at forty rows is a grid
   and reads as a spreadsheet. Rows are taller, the separator is lighter, and
   the row itself lights up under the pointer -- so the thing being pointed at
   is obvious without having to find the cursor. */
table.list{border-collapse:separate;border-spacing:0}
table.list th{
  position:sticky;top:0;z-index:2;background:var(--panel);
  font-size:.72rem;text-transform:uppercase;letter-spacing:.07em;
  padding:.6rem .65rem;border-bottom:1px solid var(--line);white-space:nowrap}
table.list td{padding:.7rem .65rem;border-bottom:1px solid var(--line);vertical-align:middle}
table.list tbody tr{transition:background .08s ease}
table.list tbody tr:hover{background:var(--hover)}
/* The title column carries the link somebody is aiming at, so it gets the
   weight. Everything else on the row is context for it. */
table.list td:first-child{font-weight:500}
table.list tbody tr:last-child td{border-bottom:0}

/* Chips. Pills that look pressable, and unmistakably filled when they are on,
   because "which filter am I looking at" is the question a queue asks most. */
.chip{
  display:inline-flex;align-items:center;gap:.3rem;
  padding:.2rem .7rem;border-radius:999px;font-size:.82rem;
  background:transparent;transition:background .08s ease,border-color .08s ease}
.chip:hover{background:var(--hover)}
.chip.on{font-weight:600}
.viewtabs{gap:.35rem;margin:.75rem 0 .25rem}

/* Cards and panels: a soft edge and a little lift, rather than a box rule. */
.card,.viewbar,.fields,.stat,.result{border-radius:10px}
.card{box-shadow:0 1px 2px rgba(0,0,0,.18)}
.viewbar{padding:.7rem .85rem}
.viewbar label{font-size:.72rem;text-transform:uppercase;letter-spacing:.06em}

/* Status and priority read as badges rather than coloured words. */
.status{
  display:inline-block;padding:.15rem .55rem;border-radius:999px;
  font-size:.72rem;font-weight:600;text-transform:capitalize;white-space:nowrap}
.tag{display:inline-block;padding:.15rem .5rem;border-radius:999px}

/* Buttons: the same shape language as the chips. */
button{border-radius:8px;transition:background .08s ease,border-color .08s ease}
button.primary{font-weight:600}

/* Focus. Keyboard users were getting the browser default over a dark panel,
   which on this background is close to invisible. */
a:focus-visible,button:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible,.chip:focus-visible{
  outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}

/* An empty list should say so. A blank area under a heading reads as a page
   that failed to load, and the difference matters most on the views somebody
   has just filtered down to nothing. */
.empty{
  padding:2.5rem 1rem;text-align:center;color:var(--muted);
  border:1px dashed var(--line);border-radius:10px;margin-top:1rem}
.empty strong{display:block;color:var(--fg);font-weight:600;margin-bottom:.25rem}

/* Board columns get the same card treatment as everything else. */
.boardcol{border-radius:10px}
.card2{border-radius:8px;transition:border-color .08s ease,background .08s ease}
.card2:hover{border-color:var(--edge)}

/* ---- the dashboard ------------------------------------------------------- */
.dash{display:flex;flex-direction:column;gap:1.5rem}

/* The week strip. Seven days, each expanding to its work in place rather than
   navigating away -- opening a day to see three jobs should not lose the other
   six days. <details> because that behaviour is native, keyboard-operable and
   needs no script on a page whose critical path deliberately has none. */
.weekstrip{display:grid;grid-template-columns:repeat(7,1fr);gap:.5rem}
.weekday{background:var(--panel);border:1px solid var(--line);border-radius:10px;
         padding:.5rem .6rem;min-width:0}
.weekday summary{list-style:none;cursor:pointer;display:flex;flex-direction:column;gap:.15rem}
.weekday summary::-webkit-details-marker{display:none}
.weekday .wd{font-size:.7rem;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}
.weekday .wdate{font-size:.9rem;font-weight:600}
.weekday .wcount{align-self:flex-start;margin-top:.2rem;background:var(--accent-dim);
                 color:#fff;border-radius:999px;padding:.05rem .45rem;
                 font-size:.72rem;font-weight:600}
/* Today is the one somebody looks for first, so it is marked by an edge rather
   than by colour alone. */
.weekday.today{border-color:var(--accent)}
.weekday[open]{grid-column:1/-1}
.weekday ul{margin:.6rem 0 0}
.weekday li{padding:.3rem 0;border-bottom:1px solid var(--line);
            display:flex;gap:.5rem;align-items:center;justify-content:space-between}
.weekday li:last-child{border-bottom:0}

/* Metric tiles are links, because a number nobody can drill into is a number
   they have to take on trust. */
a.stat{display:block;text-decoration:none;color:inherit;
       transition:border-color .08s ease,background .08s ease}
a.stat:hover{border-color:var(--edge);background:var(--hover);text-decoration:none}
a.stat dd{font-size:1.5rem;font-weight:600;letter-spacing:-.01em}

.dashgrid{display:grid;grid-template-columns:1fr 20rem;gap:1.5rem;align-items:start}
.dashgrid h2{font-size:.8rem;text-transform:uppercase;letter-spacing:.07em;
             color:var(--muted);margin:0 0 .6rem}
.watchlist li{display:flex;justify-content:space-between;gap:.75rem;
              padding:.45rem 0;border-bottom:1px solid var(--line)}
.watchlist li:last-child{border-bottom:0}

@media (max-width:64rem){
  .dashgrid{grid-template-columns:1fr}
  .weekstrip{grid-template-columns:repeat(3,1fr)}
}
@media (max-width:40rem){
  .weekstrip{grid-template-columns:repeat(2,1fr)}
}

/* Department rows in the rail. A real tree, however deep it actually goes --
   <details> per level with children, same pattern as .weekday above: no
   default marker, a row that is a link and a disclosure at once, no script.
   Indentation is structural (.orgnode .orgnode), not a fixed set of depth
   classes, so it keeps working at a depth nobody has used yet. */
.navitem.dept{font-weight:600;color:var(--fg)}
.navitem .ncount{margin-left:auto;color:var(--muted);font-size:.72rem;
                 font-family:ui-monospace,monospace}
.navitem.on .ncount{color:var(--fg)}
.orgnode>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.3rem}
.orgnode>summary::-webkit-details-marker{display:none}
.orgnode>summary::before{content:"\25B8";color:var(--muted);font-size:1.1em;
                 font-weight:700;flex:none;transition:transform .1s}
.orgnode[open]>summary::before{transform:rotate(90deg)}
.orgnode>summary>.navitem{flex:1;min-width:0}
.orgnode .orgnode{padding-left:1rem}

/* What is inside a department: a compact row, not a grid of big cards the
   same size as the work below them. Still a door into each child space --
   name, kind, item count -- just one that does not outweigh the list it
   introduces. */
.spacerow{display:flex;gap:.5rem;flex-wrap:wrap;margin:.75rem 0}
.spacechip{display:inline-flex;align-items:center;gap:.4rem;
           padding:.3rem .75rem;background:var(--panel);border:1px solid var(--edge);
           border-radius:999px;color:var(--fg);font-size:.85rem;
           transition:border-color .08s ease,background .08s ease}
.spacechip:hover{border-color:var(--accent);background:var(--hover);text-decoration:none}
.spacechip .sckind{font-size:.66rem;text-transform:uppercase;letter-spacing:.06em;
                   color:var(--muted)}
.spacechip .sccount{color:var(--muted);font-size:.75rem;font-family:ui-monospace,monospace}

/* The per-space daily-reports section on the list page -- boxed like a card
   so it reads as its own thing sitting above the ordinary document list,
   not another filter row. */
.dailyreports{background:var(--panel);border:1px solid var(--line);border-radius:8px;
              padding:.9rem 1rem;margin:.75rem 0}
.dailyreports h3{margin:0 0 .5rem}
/* The list's quick-view button, beside each task name (documents.html). */
.peekbtn{min-height:0;padding:0 .3rem;margin-right:.35rem;line-height:1.4;opacity:.7}
.peekbtn:hover,.peekbtn:focus-visible{opacity:1}
.peek .fields-row{margin:.5rem 0 1rem}
/* A comment: the author's picture (/people/{id}/avatar, initials when they
   have none -- avatar.go) beside the name, time and text. */
ul.messages li.comment{display:flex;gap:.65rem;align-items:flex-start}
.avatar{flex:0 0 auto;width:2rem;height:2rem;border-radius:50%;object-fit:cover;background:var(--line);user-select:none}
.avatar-lg{width:4.5rem;height:4.5rem}
.avatarset{gap:1rem;align-items:center;flex-wrap:wrap;margin:.5rem 0}
.commentbody{flex:1;min-width:0}
.commenthead{gap:.5rem;align-items:baseline}
li.comment .author{color:var(--accent);font-weight:700;font-size:.95rem}
li.comment .prose{margin-top:.15rem}
/* Sections that fold (Details, Attachments): the heading is the toggle. */
details.fold>summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:.4rem}
details.fold>summary::-webkit-details-marker{display:none}
details.fold>summary::before{content:"\25BE";color:var(--muted,#888);font-size:.85rem;width:1rem}
details.fold:not([open])>summary::before{content:"\25B8"}
details.fold>summary h3{margin:0}
details.fold>summary+*{margin-top:.5rem}
details.fold{margin:1rem 0}
details.fold>summary :is(h1,h2,h3,h4){cursor:pointer}
/* All documents' department picker on the Daily reports heading: one small
   line, not a full-size form field. */
.dailyreports h3 form{display:flex;align-items:center;gap:.5rem;margin:0;flex-direction:row}
.dailyreports h3 form label{margin:0;display:inline}
.dailyreports h3 select{width:auto;min-height:0;padding:.25rem .5rem;font-size:.85rem}
/* The rich-text editor (editor.js): a box that looks like the textarea it
   stands in for, as tall as the textarea's rows, growing with what is typed. */
.richbox{border:1px solid var(--edge);border-radius:8px;background:var(--input-bg,var(--bg));
  min-height:calc(var(--rows,4) * 1.5em + 1rem);max-height:60vh;overflow:auto;cursor:text}
.richbox:focus-within{border-color:var(--accent)}
.richbox-empty{border-color:var(--err)}
.richtext{outline:none;padding:.5rem .75rem;min-height:calc(var(--rows,4) * 1.5em)}
.richtext>:first-child{margin-top:0}
.richtext>:last-child{margin-bottom:0}
.richtext p.is-editor-empty:first-child::before{content:attr(data-placeholder);color:var(--muted);
  float:left;height:0;pointer-events:none}
.richtext ul[data-type=taskList]{list-style:none;padding-left:.25rem}
.richtext ul[data-type=taskList] li{display:flex;gap:.5rem;align-items:flex-start}
.richtext ul[data-type=taskList] li>label{margin:0;flex:0 0 auto}
.richtext ul[data-type=taskList] li>div{flex:1}
.mdraw-toggle{display:none}
form.rich-on .mdraw-toggle{display:inline-flex}
form.mdbig-on .richbox{flex:1;max-height:none;min-height:0}
form.mdbig-on.mdpreview-on .mdpair .richbox{flex:1 1 50%}
.richtext li>p{margin:0}
/* The playground banner (layout.html): loud on purpose, so practice work is
   never mistaken for real work. */
.playbanner{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;padding:.5rem 1.5rem;
  background:repeating-linear-gradient(135deg,#7c2d12 0 14px,#9a3412 14px 28px);color:#fff}
.playbanner strong{letter-spacing:.08em}
.playbanner a{color:#fff;text-decoration:underline}
.playbanner form{margin-left:auto}
.tutorial ul{margin:.75rem 0 1rem 1.25rem}
/* Time off (timeoff.html). */
.timeoff-item{padding:.75rem 0;border-bottom:1px solid var(--line)}
.timeoff-form{max-width:36rem}
.status.timeoff-approved{background:var(--ok);color:#000}
.status.timeoff-denied{background:var(--err);color:#000}
.status.timeoff-cancelled{opacity:.6}
.timeoff-form label.row{display:flex;align-items:center;gap:.5rem;justify-content:flex-start}
.timeoff-form label.row input[type=checkbox]{width:auto;flex:0 0 auto;min-height:0;margin:0}

/* Subtasks in a table: folded under their parent, indented by depth, the first
   cell carrying the parent's toggle. Indent on the first cell only, so the
   other columns stay lined up with their headers. */
tr.subrow>td:first-child{border-left:2px solid var(--line)}
tr.depth1>td:first-child{padding-left:1.6rem}
tr.depth2>td:first-child{padding-left:2.8rem}
tr.depth3>td:first-child,tr.depth4>td:first-child,tr.depth5>td:first-child{padding-left:4rem}
.subtoggle{min-width:2.6rem;font-variant-numeric:tabular-nums}
.subtoggle[aria-expanded="true"]{color:var(--accent,inherit)}

/* Assignee picker: one row per person, checkbox to assign, radio for primary. */
.assignrows{display:flex;flex-direction:column;gap:.1rem;max-height:18rem;overflow:auto;margin:.3rem 0}
.assignrow{display:flex;align-items:center;gap:.75rem;max-width:24rem;padding:.2rem .35rem;border-radius:.3rem}
.assignrow:hover{background:var(--hover,rgba(127,127,127,.08))}
.assignrow .aname{display:flex;align-items:center;gap:.4rem;cursor:pointer}
.assignrow .aprimary{margin-left:auto;display:flex;align-items:center;gap:.3rem;cursor:pointer;color:var(--muted,inherit);font-size:.85em;white-space:nowrap}

/* "+ folder" in the department rail. Hidden unless the Departments section
   carries can-manage (managers/admins), and only on row hover, so it stays
   quiet. The row is a flex so the + sits at the end. */
.orgnode summary{display:flex;align-items:center}
.navrow{display:flex;align-items:center}
.navrow .navitem{flex:1}
.addfolder{display:none;margin-left:auto;padding:0 .45rem;border-radius:.3rem;color:var(--muted,inherit);text-decoration:none;font-weight:600}
.can-manage .addfolder{display:inline-block;opacity:0}
.can-manage .orgnode summary:hover .addfolder,
.can-manage .navrow:hover .addfolder,
.addfolder:focus{opacity:.65}
.addfolder:hover{opacity:1;background:var(--hover,rgba(127,127,127,.12))}

/* Spaces & folders page: a clean tree, edit controls tucked behind a per-row
   "Edit" disclosure so the default view is just the hierarchy. */
.spacetree{list-style:none;margin:.5rem 0;padding:0}
.spacetree li{padding:.1rem 0}
.spacerow{display:flex;align-items:center;gap:.6rem}
.spacerow .sname{font-weight:500}
.spacerow .addfolder.shown{display:inline-block;opacity:.7;margin-left:0}
.spacerow .rowedit{margin-left:auto}
.spacerow .rowedit>summary{list-style:none;cursor:pointer;color:var(--muted,inherit)}
.spacerow .rowedit>summary::-webkit-details-marker{display:none}
.rowedit .editbody{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;margin:.3rem 0 .6rem;padding:.4rem .5rem;border-left:2px solid var(--line,rgba(127,127,127,.3))}
.ghost.small.danger{color:#c0564a}

/* Space tree drag-to-reorder. */
.spacetree li[draggable]{cursor:default}
.spacetree .draghandle{cursor:grab;color:var(--edge,#6b757e);opacity:.9;user-select:none;font-size:1.05em}
.spacetree li.dragging{opacity:.45}
.spacetree li.over{box-shadow:inset 0 2px 0 0 var(--accent,#c0564a)}

/* Save bar for a staged space reorder: sticks to the bottom until saved. */
.orderbar{position:sticky;bottom:0;display:flex;align-items:center;gap:.75rem;
  margin:.5rem 0;padding:.6rem .8rem;border-radius:.4rem;
  background:var(--surface,#1b1b1b);border:1px solid var(--line,rgba(127,127,127,.3));box-shadow:0 -2px 8px rgba(0,0,0,.2)}
.orderbar>span{flex:1}

/* + New department link on the Spaces page. */
.newdept{display:inline-block;padding:.25rem .6rem;border-radius:.3rem;font-weight:600;
  text-decoration:none;color:var(--muted,inherit);border:1px solid var(--line,rgba(127,127,127,.3))}
.newdept:hover{color:inherit;background:var(--hover,rgba(127,127,127,.12))}

/* Spaces tree: collapse caret, department colour, nesting guide lines. */
.treetoggle{display:inline-flex;align-items:center;justify-content:center;
  width:1.5rem;height:1.5rem;padding:0;line-height:1;font-size:1.1em;cursor:pointer;
  color:var(--fg);background:var(--hover);border:1px solid var(--line-strong,#6b757e);border-radius:50%}
.treetoggle:hover{background:var(--surface);border-color:var(--edge)}
.treespacer{display:inline-block;width:1.5rem}
.sname.dept{color:var(--accent,#e0794a);font-weight:700}
.spacetree li:not([data-depth="0"]) .sname{color:var(--muted,inherit);font-weight:500}
.spacetree li[data-depth="0"]{margin-top:.3rem}
/* Faint vertical guides at each nesting level; consecutive rows stack into a line. */
.spacetree li{position:relative}
.spacetree li[data-depth="1"]::before,
.spacetree li[data-depth="2"]::before,
.spacetree li[data-depth="3"]::before,
.spacetree li[data-depth="4"]::before{content:"";position:absolute;top:0;bottom:0;
  border-left:1px solid var(--line-strong,#6b757e)}
.spacetree li[data-depth="1"]::before{left:.55rem}
.spacetree li[data-depth="2"]::before{left:1.65rem}
.spacetree li[data-depth="3"]::before{left:2.75rem}
.spacetree li[data-depth="4"]::before{left:3.85rem}
