/*
 * portal.css — the SHAPE of every Helpdesk page. No colour is decided here.
 *
 * Every colour, radius, shadow and font comes from brand.css, which the
 * marketing pages read too. Put a hex value in this file and the portal stops
 * following the website's redesign, which is the whole reason brand.css
 * exists.
 *
 * These rules used to live in a <style> block inside layout.php, sent again
 * with every page. They moved out here on 2026-10-09 when the staff app shell
 * nearly doubled their size: a browser now fetches them once and reuses them,
 * and a future Content-Security-Policy does not have to make an exception for
 * inline styles.
 *
 * TWO LAYOUTS LIVE HERE
 *   .wrap   a single centred card. The customer's pages and both sign-ins.
 *           Someone who visits a few times a year wants one thing in front of
 *           them, not a navigation system.
 *   .app    sidebar + main column. The team's pages. Somebody who works in
 *           this all day needs every screen one click away.
 *
 * Cache: layout.php stamps ?v=HD_ASSET_V on the link. Bump it when you edit
 * this file, or browsers keep yesterday's copy.
 */

*{box-sizing:border-box}
html{background:var(--surface)}
body{
  margin:0;min-height:100vh;color:var(--text);
  font-family:var(--font);font-size:var(--font-size);line-height:var(--line-height);
  background-color:var(--surface);
  background-image:var(--surface-wash);
  background-attachment:fixed;
}

/* ===================================================================== */
/* The centred card — customer pages and the sign-ins                    */
/* ===================================================================== */

.wrap{max-width:27rem;margin:0 auto;padding:3rem 1rem 2rem}
.wrap.wide{max-width:60rem}

/* The site header's wordmark, so a customer sees the mark they just
   clicked away from. */
.brand{text-align:center;margin-bottom:1.5rem}
.brand .mark{font-size:1.4rem;font-weight:700;letter-spacing:.06em;
             color:var(--text-strong);text-decoration:none;display:inline-block}
.brand .mark span{color:var(--accent)}
.brand .what{display:block;margin-top:.35rem;color:var(--text-muted);font-size:15px}

.card{background:var(--glass);backdrop-filter:var(--glass-blur);
      -webkit-backdrop-filter:var(--glass-blur);
      border:1px solid var(--line);border-radius:var(--radius);
      padding:1.75rem 1.5rem;box-shadow:var(--shadow)}

/* ===================================================================== */
/* Shared pieces — used by both layouts                                  */
/* ===================================================================== */

h1{font-size:1.05rem;margin:0 0 .25rem;color:var(--text-strong)}
.sub{color:var(--text-muted);margin:0 0 1.25rem;font-size:15px}
.hint{color:var(--text-muted);font-size:14px;margin-top:.3rem}

label{display:block;margin:.85rem 0 .3rem;font-weight:500;color:var(--text-strong)}
input[type=text],input[type=email],input[type=password]{
    width:100%;padding:.6rem .7rem;border:1px solid var(--line-strong);
    border-radius:var(--radius-sm);font-family:inherit;font-size:var(--font-size);
    background:var(--field);color:var(--text-strong)}
input::placeholder{color:var(--text-muted)}
input:focus{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
.check{display:flex;align-items:center;gap:.5rem;margin:.9rem 0 0;font-weight:400;color:var(--text)}
.check input{width:auto;margin:0;accent-color:var(--accent)}

button{width:100%;margin-top:1.1rem;padding:.65rem;border:0;border-radius:var(--radius-sm);
       background:var(--accent);color:var(--on-accent);font-family:inherit;
       font-size:var(--font-size);font-weight:700;cursor:pointer}
button:hover{background:var(--accent-hover)}
button:focus-visible{outline:2px solid var(--text-strong);outline-offset:2px}

.msg{padding:.6rem .75rem;border-radius:var(--radius-sm);margin-bottom:1rem;
     font-size:15px;border:1px solid var(--line)}
.msg.err{background:var(--danger-bg);color:var(--danger);border-color:var(--danger-line)}
.msg.ok{background:var(--ok-bg);color:var(--ok);border-color:var(--ok-line)}
/* A request for information is not an error, so it is not red. */
.msg.ask{background:var(--glass);color:var(--accent);border-color:var(--accent);font-weight:500}

.foot{margin-top:1.25rem;text-align:center;color:var(--text-muted);font-size:14px;line-height:1.7}
.foot a{color:var(--accent);text-decoration:none}
.foot a:hover{text-decoration:underline;color:var(--accent-hover)}

.bar{display:flex;justify-content:space-between;align-items:center;gap:1rem;
     margin-bottom:1rem;font-size:15px;color:var(--text-muted);flex-wrap:wrap}
.bar strong{color:var(--text-strong)}
.bar form{margin:0}
.bar button{width:auto;margin:0;padding:.3rem .8rem;font-size:15px;font-weight:500;
            background:transparent;color:var(--accent);border:1px solid var(--line-strong)}
.bar button:hover{background:var(--glass);color:var(--accent-hover)}

table.sys{width:100%;border-collapse:collapse;margin-top:.5rem}
table.sys th,table.sys td{text-align:left;padding:.55rem .6rem;
                          border-bottom:1px solid var(--line);vertical-align:top}
table.sys th{color:var(--text-muted);font-weight:500;font-size:14px}
table.sys strong{color:var(--text-strong)}
.pill{display:inline-block;padding:.1rem .5rem;border-radius:999px;
      background:var(--glass);border:1px solid var(--line-strong);
      color:var(--accent);font-size:14px}

select,textarea{
    width:100%;padding:.6rem .7rem;border:1px solid var(--line-strong);
    border-radius:var(--radius-sm);font-family:inherit;font-size:var(--font-size);
    background:var(--field);color:var(--text-strong)}
textarea{line-height:1.6;resize:vertical;min-height:6rem}
select:focus,textarea:focus{outline:2px solid var(--accent);outline-offset:1px;
    border-color:var(--accent)}
/* The dropdown list itself is drawn by the operating system, which on a
   dark page would otherwise put dark text on a dark background. */
select option{background:var(--surface-raised);color:var(--text-strong)}
input[type=file]{width:100%;padding:.5rem 0;color:var(--text);font-family:inherit;font-size:15px}
input[type=file]::file-selector-button{
    margin-right:.75rem;padding:.4rem .8rem;border:1px solid var(--line-strong);
    border-radius:var(--radius-sm);background:transparent;color:var(--accent);
    font-family:inherit;font-size:15px;cursor:pointer}

h2.h2{font-size:.95rem;margin:1.5rem 0 .5rem;padding-top:1rem;
      border-top:1px solid var(--line);color:var(--text-strong)}

.tabs{display:flex;gap:.4rem;flex-wrap:wrap;margin:.75rem 0 .25rem}
.tab{padding:.3rem .8rem;border:1px solid var(--line-strong);border-radius:999px;
     color:var(--text-muted);text-decoration:none;font-size:15px}
.tab:hover{color:var(--accent);border-color:var(--accent)}
.tab.on{background:var(--accent);color:var(--on-accent);border-color:var(--accent);font-weight:700}

/* One message in the conversation. The team's replies are marked with a
   coloured edge rather than a different background, so a long thread does
   not turn into stripes. */
.post{border:1px solid var(--line);border-radius:var(--radius-sm);
      padding:.75rem .9rem;margin:.6rem 0;background:var(--glass)}
.post.from-ws{border-left:3px solid var(--accent)}
/* An internal note is marked three ways at once - a coloured edge, a
   hatched background and a label saying who cannot see it - because the
   cost of mistaking one for a reply to the customer is high. */
.post.internal{border-left:3px solid var(--danger);
               background:repeating-linear-gradient(135deg,
                 var(--danger-bg) 0 10px, transparent 10px 20px)}
.pill.tag-internal{color:var(--danger);border-color:var(--danger-line);
                   background:var(--danger-bg);margin-left:.4rem}
textarea.internal-box{border-color:var(--danger-line);background:var(--danger-bg)}
button.secondary{background:transparent;color:var(--accent);
                 border:1px solid var(--line-strong);font-weight:500}
button.secondary:hover{background:var(--glass);color:var(--accent-hover)}
.post .who{font-weight:700;color:var(--text-strong);font-size:15px;margin-bottom:.3rem}
.post .when{font-weight:400;color:var(--text-muted);font-size:14px;margin-left:.5rem}
.post .body{word-break:break-word}
.post .files{margin-top:.5rem;font-size:15px}
.post .files a{color:var(--accent);text-decoration:none;margin-right:.3rem}
.post .files a:hover{text-decoration:underline}

/* Status is carried by the wording; the colour only reinforces it, so the
   list still reads for anyone who cannot separate the hues. */
.pill.status-closed{color:var(--text-muted);border-color:var(--line)}
.pill.status-waiting{color:var(--ok);border-color:var(--ok-line);background:var(--ok-bg)}
.pill.status-new{color:var(--on-accent);background:var(--accent);
                 border-color:var(--accent);font-weight:700}

/* An action inside a table row has to be a real form button (it POSTs
   and carries a CSRF token) but should read as a link beside the others. */
button.linkish{width:auto;margin:0 0 0 .6rem;padding:0;border:0;background:none;
               color:var(--accent);font:inherit;font-weight:400;cursor:pointer;
               text-decoration:underline}
button.linkish:hover{color:var(--accent-hover)}
.picklist{max-height:14rem;overflow:auto;border:1px solid var(--line-strong);
          border-radius:var(--radius-sm);padding:.5rem .7rem;background:var(--field)}
.picklist .check{margin:.2rem 0}
/* A report can be wider than a phone; let it scroll rather than squash
   the columns until the numbers are unreadable. */
.scrollx{overflow-x:auto;-webkit-overflow-scrolling:touch}
.scrollx table.sys{min-width:40rem}
.rangeform{display:flex;align-items:flex-end;gap:.6rem;flex-wrap:wrap;margin:.75rem 0}
.rangeform label{margin:0 0 .3rem}
.rangeform input{width:9rem}
.rangeform button{margin-top:0;width:auto;padding:.55rem 1rem}
a.btnlink{display:inline-block;padding:.55rem 1.1rem;border-radius:var(--radius-sm);
          background:var(--accent);color:var(--on-accent);text-decoration:none;font-weight:700}
a.btnlink:hover{background:var(--accent-hover)}
/* The same button sized for the title bar, where it sits beside a heading
   rather than at the end of a page. */
a.btnlink.sm{padding:.35rem .85rem;font-size:15px}

/* Pages use these instead of carrying styles of their own. */
/* A form that has to sit on one line with something else. It replaces
   style="display:inline" written into the markup, which would have meant
   allowing inline CSS in the Content-Security-Policy. */
.inline{display:inline}
/* A button that should be its own width - in a toolbar or a title bar -
   rather than the full-width default a form button has. */
button.wauto{width:auto;margin:0;padding:.35rem .85rem;font-size:15px}
/* A long URL in a table cell: shown, but not allowed to set the column
   width for every other row. */
a.trunc{display:inline-block;max-width:18rem;overflow:hidden;
        text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom;
        color:var(--accent);text-decoration:none}
a.trunc:hover{text-decoration:underline;color:var(--accent-hover)}
.note-danger{color:var(--danger)}
.hint.warn{color:var(--danger)}
.nowrap{white-space:nowrap}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
.privacy{margin-bottom:.4rem}
.mt{margin-top:1rem}
.section-end{margin-top:1.5rem;padding-top:1rem;border-top:1px solid var(--line)}

/* An icon beside a label. Drawn with currentColor so it takes the colour of
   whatever it sits in and needs no variant per context. */
.ico{width:1em;height:1em;flex:none;vertical-align:-.12em;
     stroke:currentColor;fill:none;stroke-width:1.6;
     stroke-linecap:round;stroke-linejoin:round}

/* ===================================================================== */
/* The staff app shell — sidebar and main column                         */
/* ===================================================================== */

.app{display:flex;min-height:100vh;align-items:stretch}

.side{
  width:15rem;flex:none;display:flex;flex-direction:column;gap:.25rem;
  padding:1rem .75rem 1.25rem;
  background:var(--glass);backdrop-filter:var(--glass-blur);
  -webkit-backdrop-filter:var(--glass-blur);
  border-right:1px solid var(--line);
}
/* Who is signed in. First thing in the sidebar for the same reason a shop
   till shows the cashier's name: an internal note written by the wrong
   account is worse than no note. */
.side .me{display:flex;align-items:center;gap:.55rem;padding:.4rem .55rem .7rem}
.side .me .av{width:1.9rem;height:1.9rem;flex:none;border-radius:999px;
              background:var(--accent);color:var(--on-accent);
              display:flex;align-items:center;justify-content:center;
              font-size:13px;font-weight:700;letter-spacing:.02em}
.side .me .nm{min-width:0}
.side .me .nm b{display:block;color:var(--text-strong);font-size:15px;font-weight:600;
                overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side .me .nm span{display:block;color:var(--text-muted);font-size:13px}

.side .grp{margin:1rem .55rem .25rem;color:var(--text-muted);
           font-size:13px;letter-spacing:.04em;text-transform:uppercase}

.side a.nav{display:flex;align-items:center;gap:.6rem;
            padding:.45rem .55rem;border-radius:var(--radius-sm);
            color:var(--text);text-decoration:none;font-size:15px}
.side a.nav:hover{background:var(--glass);color:var(--text-strong)}
/* The page you are on. A filled block rather than a coloured word, so it
   reads as "you are here" and not as "this one is special". */
.side a.nav.on{background:var(--surface-raised);color:var(--text-strong);font-weight:600}
.side a.nav .lbl{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side a.nav .n{flex:none;color:var(--text-muted);font-size:14px;font-variant-numeric:tabular-nums}
.side a.nav.on .n{color:var(--text-strong)}
/* Nothing waiting is worth seeing at a glance, so zero is dimmed, not hidden -
   a missing number reads as "not loaded". */
.side a.nav .n.zero{opacity:.45}

.side .side-end{margin-top:auto;padding-top:1rem;border-top:1px solid var(--line);
                display:flex;flex-direction:column;gap:.35rem}
.side .side-end a{color:var(--text-muted);text-decoration:none;font-size:14px;padding:0 .55rem}
.side .side-end a:hover{color:var(--accent)}
.side .side-end form{margin:.25rem .55rem 0}
.side .side-end button{width:100%;margin:0;padding:.4rem;font-size:14px;font-weight:500;
                       background:transparent;color:var(--text-muted);
                       border:1px solid var(--line-strong)}
.side .side-end button:hover{background:var(--glass);color:var(--accent)}

.main{flex:1;min-width:0;display:flex;flex-direction:column}

.topbar{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;
        padding:.9rem 1.25rem;border-bottom:1px solid var(--line)}
/* A ticket's title is its number and its subject, which can run long - it is
   cut here rather than allowed to push the page sideways. */
.topbar h1{font-size:1rem;margin:0;min-width:0;max-width:100%;
           overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.topbar .spacer{flex:1}

.toolbar{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;
         padding:.7rem 1.25rem;border-bottom:1px solid var(--line)}
.toolbar .spacer{flex:1}
.toolbar .lab{color:var(--text-muted);font-size:14px;margin-right:.15rem}
/* A filter and a view switch are links, not buttons: they only change what is
   shown, they change nothing, and a link can be bookmarked and shared.
   Defined once here and used by the staff toolbar and by the customer's
   ticket list, so the two cannot drift apart. */
a.chip{display:inline-flex;align-items:center;gap:.35rem;
       padding:.28rem .7rem;border:1px solid var(--line-strong);
       border-radius:var(--radius-sm);color:var(--text-muted);
       text-decoration:none;font-size:14px;white-space:nowrap}
a.chip:hover{color:var(--accent);border-color:var(--accent)}
a.chip.on{background:var(--surface-raised);color:var(--text-strong);
          border-color:var(--line-strong);font-weight:600}
.toolbar .seg{display:inline-flex;border:1px solid var(--line-strong);
              border-radius:var(--radius-sm);overflow:hidden}
.toolbar .seg a.chip{border:0;border-radius:0}
.toolbar .seg a.chip+a.chip{border-left:1px solid var(--line-strong)}

.sheet{padding:1.25rem;flex:1;min-width:0}
/* A separator is a line BETWEEN two things. At the very top of the sheet
   there is nothing above it to separate from, and it reads as a stray rule -
   which is what the admin screens showed once their row of tabs moved into
   the sidebar and left these as the first element on the page. */
.sheet>h2.h2:first-child,
.sheet>.section-end:first-child{margin-top:0;padding-top:0;border-top:0}
/* A page of prose and form fields, capped at a readable measure. A board or a
   table wants the whole screen; a reply box 1300 px wide is a reply box
   nobody can read back, and a submit button that wide looks like a mistake. */
.sheet.reading{max-width:58rem}

/* ===================================================================== */
/* A record page: the thread on the left, its properties on the right    */
/* ===================================================================== */

/*
 * The controls used to sit under the conversation, so the longer a ticket
 * ran the further they slid off the bottom - and a ticket that has run long
 * is exactly the one somebody needs to reassign or mark chargeable. The
 * right-hand column stays put while the thread scrolls past it.
 */
.two{display:flex;gap:1.5rem;align-items:flex-start;max-width:76rem}
.two>.thread{flex:1;min-width:0}
.two>.props{width:20rem;flex:none;position:sticky;top:1rem}
.two>.props .pblock:first-child h2.h2{margin-top:0;padding-top:0;border-top:0}

/* Facts read as label-above-value here rather than as a two-column table:
   at 20rem a table puts "ผู้แจ้ง" and an e-mail address in the same row and
   neither one fits. */
.facts{margin:.25rem 0 1rem}
.facts dt{color:var(--text-muted);font-size:14px;margin-top:.7rem}
.facts dd{margin:.1rem 0 0;color:var(--text);word-break:break-word}
.facts dd:first-of-type{margin-top:0}

/* ===================================================================== */
/* The customer shell — navigation, their tickets, the conversation      */
/* ===================================================================== */

.cwrap{display:grid;grid-template-columns:15rem minmax(19rem,23rem) minmax(0,1fr);
       min-height:100vh;align-items:stretch}
/* A page with no middle column — the reporting form — gives the space to
   the form instead of leaving a gap where a list would have been. */
.cwrap.nolist{grid-template-columns:15rem minmax(0,1fr)}

.cnav{display:flex;flex-direction:column;gap:.2rem;padding:1.25rem .75rem 1.25rem;
      background:var(--glass);backdrop-filter:var(--glass-blur);
      -webkit-backdrop-filter:var(--glass-blur);border-right:1px solid var(--line)}
.cnav .mark{font-size:1.1rem;font-weight:700;letter-spacing:.05em;padding:0 .55rem;
            color:var(--text-strong);text-decoration:none}
.cnav .mark span{color:var(--accent)}
.cnav>.what{display:block;padding:0 .55rem;margin-bottom:1rem;
            color:var(--text-muted);font-size:14px}
.cnav .me{display:flex;align-items:center;gap:.55rem;padding:.4rem .55rem .9rem}
.cnav .me .av{width:1.9rem;height:1.9rem;flex:none;border-radius:999px;
              background:var(--accent);color:var(--on-accent);
              display:flex;align-items:center;justify-content:center;
              font-size:13px;font-weight:700}
.cnav .me .nm{min-width:0}
.cnav .me .nm b{display:block;color:var(--text-strong);font-size:15px;font-weight:600;
                overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cnav .me .nm span{display:block;color:var(--text-muted);font-size:13px;
                   overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.btnlink.block{display:block;text-align:center;margin:0 .55rem .75rem}
.cnav a.nav{display:flex;align-items:center;gap:.6rem;padding:.45rem .55rem;
            border-radius:var(--radius-sm);color:var(--text);
            text-decoration:none;font-size:15px}
.cnav a.nav:hover{background:var(--glass);color:var(--text-strong)}
.cnav a.nav.on{background:var(--surface-raised);color:var(--text-strong);font-weight:600}
.cnav .grp{margin:1.25rem .55rem .3rem;color:var(--text-muted);
           font-size:13px;letter-spacing:.04em;text-transform:uppercase}
.cnav .sys-item{padding:.3rem .55rem;color:var(--text);font-size:15px}
.cnav .sys-item .hint{display:block;margin-top:0}
.cnav .side-end{margin-top:auto;padding-top:1rem;border-top:1px solid var(--line);
                display:flex;flex-direction:column;gap:.35rem}
.cnav .side-end a{color:var(--text-muted);text-decoration:none;font-size:14px;padding:0 .55rem}
.cnav .side-end a:hover{color:var(--accent)}
.cnav .side-end form{margin:.3rem .55rem 0}
.cnav .side-end button{width:100%;margin:0;padding:.4rem;font-size:14px;font-weight:500;
                       background:transparent;color:var(--text-muted);
                       border:1px solid var(--line-strong)}
.cnav .side-end button:hover{background:var(--glass);color:var(--accent)}

.clist{border-right:1px solid var(--line);min-width:0;display:flex;flex-direction:column}
.clist .head{padding:.9rem 1rem;border-bottom:1px solid var(--line);
             display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.clist .head h1{font-size:1rem;margin:0;flex:1}
.clist .items{padding:.6rem;display:flex;flex-direction:column;gap:.4rem}
.clist .none{padding:1.5rem 1rem;color:var(--text-muted);font-size:15px}

/* One ticket in the middle column. The whole card is the link. */
a.citem{display:block;padding:.7rem .8rem;border:1px solid transparent;
        border-radius:var(--radius-sm);color:var(--text);text-decoration:none}
a.citem:hover{background:var(--glass);border-color:var(--line)}
a.citem.on{background:var(--surface-raised);border-color:var(--line-strong)}
a.citem .top{display:flex;align-items:baseline;gap:.5rem}
a.citem .who{flex:1;min-width:0;color:var(--text-strong);font-size:15px;font-weight:600;
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.citem .when{flex:none;color:var(--text-muted);font-size:13px}
a.citem .subj{display:block;margin:.1rem 0;color:var(--text);font-size:15px;
              overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.citem .snip{display:block;color:var(--text-muted);font-size:14px;line-height:1.5;
              overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
a.citem .tags{display:flex;gap:.3rem;flex-wrap:wrap;margin-top:.45rem}
/* An unanswered question from us is the one thing in this list that needs
   the customer to act, so it is the only thing drawn in the accent. */
a.citem .dot{display:inline-block;width:.45rem;height:.45rem;border-radius:999px;
             background:var(--accent);flex:none}

.cview{min-width:0;display:flex;flex-direction:column}
.cview .head{padding:.9rem 1.25rem;border-bottom:1px solid var(--line)}
.cview .head h1{font-size:1rem;margin:0 0 .25rem;
                overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cview .body{padding:1.25rem;flex:1;min-width:0;max-width:52rem}
.cview .empty{padding:4rem 1.25rem;text-align:center;color:var(--text-muted);font-size:15px}
/* The reply box sits under the thread, against a rule, so a long
   conversation does not push it out of reach of the eye. */
.cview .replybox{margin-top:1.5rem;padding-top:1.1rem;border-top:1px solid var(--line)}
/* Send is its own width. A button as wide as the pane reads as a banner. */
.cview .replybox button{width:auto;padding:.6rem 1.9rem}
.cview .back{display:none}

/* ---- narrow screens ------------------------------------------------- */

/* Below this the navigation becomes a row across the top and the two
   remaining columns share the width. */
@media (max-width:1100px){
  .cwrap,.cwrap.nolist{display:flex;flex-direction:column}
  .cnav{flex-direction:row;flex-wrap:wrap;align-items:center;gap:.4rem;
        border-right:0;border-bottom:1px solid var(--line);padding:.6rem .75rem}
  .cnav>.what,.cnav .grp,.cnav .sys-item{display:none}
  .cnav .me{padding:.2rem .4rem}
  a.btnlink.block{display:inline-block;margin:0 .4rem 0 0}
  .cnav a.nav{padding:.3rem .6rem}
  .cnav .side-end{margin:0 0 0 auto;padding:0;border-top:0;
                  flex-direction:row;align-items:center;gap:.6rem}
  .cnav .side-end form{margin:0}
  .cnav .side-end button{width:auto;padding:.25rem .7rem}
  .clist{border-right:0;border-bottom:1px solid var(--line)}
}

/* One pane at a time on a phone: the list when you are choosing, the
   conversation when you are reading it. */
@media (max-width:780px){
  .cwrap.on-view .clist{display:none}
  .cwrap.on-list .cview{display:none}
  .cview .back{display:block}
  .cview .body{padding:.9rem}
}

/* ===================================================================== */
/* Record forms                                                          */
/* ===================================================================== */

/*
 * A form used to be one column of full-width boxes, so a date and a phone
 * number were each as wide as a paragraph and the eye had nowhere to rest.
 * Fields now sit in two columns, grouped under headings, and only the ones
 * that genuinely need the width take it.
 */
.fsec{margin-top:1.75rem;padding-top:1.1rem;border-top:1px solid var(--line)}
.fsec:first-of-type{margin-top:.75rem;padding-top:0;border-top:0}
.fsec>h3{font-size:.92rem;font-weight:600;margin:0;color:var(--text-strong)}
.fsec>.lead{color:var(--text-muted);font-size:14px;margin:.15rem 0 0}

.fgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 1.5rem}
.fgrid .f{min-width:0}
/* An address, a list of companies, a note: things that are hurt by being
   cut in half. */
.fgrid .f.wide{grid-column:1 / -1}
.fgrid .f label:first-child{margin-top:.95rem}

/* The save button belongs on its own line under a rule, not floating
   directly beneath the last field as though it were another one. */
.factions{display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;
          margin-top:1.75rem;padding-top:1.1rem;border-top:1px solid var(--line)}
.factions button{width:auto;margin:0;padding:.6rem 1.8rem}
.factions a{color:var(--text-muted);text-decoration:none;font-size:15px}
.factions a:hover{color:var(--accent)}

/* One column once two would make each of them too narrow to read. */
@media (max-width:760px){ .fgrid{grid-template-columns:minmax(0,1fr)} }

/* ===================================================================== */
/* The administration overview                                           */
/* ===================================================================== */

.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(9.5rem,1fr));
       gap:.7rem;margin:.25rem 0 .5rem}
a.tile,div.tile{display:block;padding:.8rem .9rem;border:1px solid var(--line);
       border-radius:var(--radius-sm);background:var(--glass);
       color:var(--text);text-decoration:none}
a.tile:hover{border-color:var(--accent)}
.tile .n{display:block;font-size:1.6rem;line-height:1.15;font-weight:700;
         color:var(--text-strong);font-variant-numeric:tabular-nums}
.tile .lb{display:block;margin-top:.15rem;color:var(--text-muted);font-size:14px}
/* A number that means somebody has to do something, rather than a number
   that is merely large. Nothing is red just for being big. */
.tile.warn .n{color:var(--danger)}
.tile.warn{border-color:var(--danger-line);background:var(--danger-bg)}
.tile.ok .n{color:var(--ok)}

/* Two panels side by side on a wide screen, stacked on a narrow one. */
.panels{display:grid;grid-template-columns:repeat(auto-fit,minmax(20rem,1fr));
        gap:1.25rem;margin-top:.5rem}
.panel{min-width:0}
.panel h2.h2:first-child{margin-top:0}

/* A bar is an SVG rect, not a div with a width in a style attribute, so the
   page still carries no inline CSS for a Content-Security-Policy to forgive. */
.bars{display:flex;flex-direction:column;gap:.45rem;margin-top:.5rem}
.bars .row{display:flex;align-items:center;gap:.6rem;font-size:15px}
.bars .row .lb{flex:0 0 9.5rem;min-width:0;overflow:hidden;
               text-overflow:ellipsis;white-space:nowrap;color:var(--text)}
.bars .row svg{flex:1;height:.55rem;min-width:0}
.bars .row svg rect{fill:var(--accent)}
.bars .row .n{flex:none;width:2.2rem;text-align:right;color:var(--text-muted);
              font-variant-numeric:tabular-nums}

.kv{display:flex;justify-content:space-between;gap:1rem;padding:.4rem 0;
    border-bottom:1px solid var(--line);font-size:15px}
.kv:last-child{border-bottom:0}
.kv .k{color:var(--text-muted)}
.kv .v{color:var(--text-strong);text-align:right;font-variant-numeric:tabular-nums}
.kv .v.bad{color:var(--danger)}
.kv .v.good{color:var(--ok)}

/* ===================================================================== */
/* The board                                                             */
/* ===================================================================== */

.board{display:flex;gap:.9rem;align-items:flex-start;
       overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:.5rem}
/* 15rem, not more: five of these plus the sidebar have to fit a 1600px
   laptop without the last column hanging off the edge. */
.col{width:15rem;flex:none;display:flex;flex-direction:column;min-width:0}
.col .ch{display:flex;align-items:center;gap:.45rem;padding:0 .2rem .55rem}
.col .ch .nm{color:var(--text-strong);font-size:15px;font-weight:600;
             overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.col .ch .n{margin-left:auto;color:var(--text-muted);font-size:14px;
            font-variant-numeric:tabular-nums}
/* The dot repeats what the heading already says, in the same three groups the
   status pills use. It is reinforcement, never the only signal. */
.col .ch .dot{width:.5rem;height:.5rem;flex:none;border-radius:999px;
              border:1.5px solid var(--text-muted)}
.col .ch .dot.status-new{background:var(--accent);border-color:var(--accent)}
.col .ch .dot.status-open{border-color:var(--text)}
.col .ch .dot.status-waiting{background:var(--ok);border-color:var(--ok)}
.col .ch .dot.status-closed{border-color:var(--line-strong)}
.col .cards{display:flex;flex-direction:column;gap:.5rem;min-width:0}
/* Named .empty, not .none: a card inside this column uses .meta.none for the
   unassigned line, and a descendant selector would have drawn this dashed box
   around it. */
.col .empty{color:var(--text-muted);font-size:14px;padding:.6rem .2rem;
            border:1px dashed var(--line);border-radius:var(--radius-sm);text-align:center}
.col .sum{margin-top:.6rem;padding-top:.5rem;border-top:1px solid var(--line);
          color:var(--text-muted);font-size:14px;display:flex;gap:.4rem;align-items:center}

/* One ticket. The whole card is the link, so there is no small target to aim
   at and no part of it that looks clickable but is not. */
/* min-width:0 because a card is a flex item, and a flex item refuses by
   default to shrink below the width of its own content. The company line
   inside is one unbreakable nowrap string, so without this the card stays as
   wide as that string and pushes the whole page sideways on a phone. */
a.tcard{display:block;min-width:0;max-width:100%;
        padding:.7rem .75rem;border:1px solid var(--line);
        border-radius:var(--radius-sm);background:var(--glass);
        color:var(--text);text-decoration:none}
a.tcard:hover{border-color:var(--accent)}
a.tcard:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
a.tcard .no{color:var(--text-muted);font-size:13px;letter-spacing:.03em}
a.tcard .ttl{display:block;margin:.1rem 0 .5rem;color:var(--text-strong);
             font-size:15px;font-weight:600;line-height:1.45;
             overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
a.tcard .meta{display:flex;align-items:center;gap:.4rem;min-width:0;
              color:var(--text-muted);font-size:14px;margin-top:.22rem}
a.tcard .meta .txt{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
a.tcard .meta.none{color:var(--danger)}
a.tcard .flags{display:flex;gap:.3rem;flex-wrap:wrap;margin-top:.5rem}
a.tcard .flag{padding:.05rem .4rem;border-radius:999px;border:1px solid var(--line-strong);
              font-size:13px;color:var(--text-muted)}
a.tcard .flag.hot{color:var(--danger);border-color:var(--danger-line);background:var(--danger-bg)}

/* ===================================================================== */
/* Narrow screens                                                        */
/* ===================================================================== */

/* Below this the sidebar becomes a row across the top and the queue is always
   a list. Five columns on a phone is a board nobody can read: you scroll
   sideways looking for a column instead of reading the work. */
@media (max-width:900px){
  .app{display:block}
  .side{width:auto;border-right:0;border-bottom:1px solid var(--line);
        flex-direction:row;flex-wrap:wrap;align-items:center;gap:.3rem;
        padding:.6rem .75rem}
  .side .me{padding:.2rem .4rem;margin-right:.4rem}
  .side .grp{display:none}
  .side a.nav{padding:.3rem .6rem;font-size:14px}
  .side .side-end{margin:0 0 0 auto;padding:0;border-top:0;
                  flex-direction:row;align-items:center;gap:.5rem}
  .side .side-end form{margin:0}
  .side .side-end button{padding:.25rem .6rem}
  .topbar,.toolbar{padding:.7rem .9rem}
  .sheet{padding:.9rem}
  .board{display:block;overflow:visible}
  .col{width:auto}
  .col+.col{margin-top:1.1rem}
}

/*
 * The record page splits later than the rest, because two columns are still
 * comfortable on a small laptop.
 *
 * In one column the order is not simply "thread then sidebar". What the
 * ticket IS goes first, because it is four short lines and it is what you
 * check before reading anything. The conversation follows. What you can DO to
 * the ticket goes last, because those controls are half a screen tall and
 * nobody should scroll past them to reach the first message.
 *
 * display:contents dissolves the right-hand column so its two blocks become
 * siblings of the thread and can be ordered around it. There is no JavaScript
 * here and no duplicated markup: the same HTML reads in a different order.
 */
@media (max-width:1000px){
  .two{display:flex;flex-direction:column;gap:0}
  .two>.thread{order:0}
  .two>.props{display:contents}
  .two .facts-block{order:-1;margin-bottom:1.5rem}
  .two .manage-block{order:1;margin-top:1.5rem}
}

@media (max-width:420px){ .wrap{padding:2rem .9rem 1.5rem} .card{padding:1.25rem 1rem} }
@media (prefers-reduced-motion:reduce){ *{transition:none!important;animation:none!important} }

/* The copy-to box on the companies screen: one short form per row, so an
   administrator can see every company at once instead of opening nineteen
   pages to change one field each. The input is styled by the element rule
   above, which makes it full width; here it shares the row with its button. */
.ccform{display:flex;gap:.5rem;align-items:center}
.ccform input[type=text]{margin:0;flex:1 1 auto;min-width:0}
.ccform button.wauto{flex:none}
/* Give the width to the box people type into. The three counts need only
   enough room for a number, and width:1% with nowrap is how a table is told
   to shrink a column to its content. */
table.sys.companies th:nth-child(2),
table.sys.companies td:nth-child(2){width:42%}
table.sys.companies th:nth-child(n+3),
table.sys.companies td:nth-child(n+3){width:1%;white-space:nowrap}

/* ------------------------------------------- filtering and paging a list ---
   The search box and the company list sit in the toolbar beside the filter
   chips, so the whole row reads as one set of controls. No JavaScript: this
   is a GET form with a button, which also means the back button, bookmarks
   and opening a filter in a new tab all work without anybody writing code. */
.listform{display:flex;gap:.4rem;align-items:center;flex-wrap:wrap}
.listform input[type=search],.listform select{margin:0;width:auto;min-width:9rem;
  padding:.3rem .55rem;font-size:15px}
.listform input[type=search]{min-width:12rem}
.listform button.wauto{margin:0}

.pager{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;
  margin-top:1rem;padding-top:.8rem;border-top:1px solid var(--line)}
.pager .pages{display:flex;align-items:center;gap:.3rem;flex-wrap:wrap;margin-left:auto}
.pager .gap{color:var(--text-muted);padding:0 .15rem}

/* ===================================================================== */
/* The sign-in pages — a panel beside the form                           */
/* ===================================================================== */
/* Scoped to .auth throughout, so the signed-in screens and the privacy
   notice keep the plain centred card they had. */
.auth{display:grid;grid-template-columns:1.05fr .95fr;min-height:100vh}

/* The panel. Three stacked layers and no image file: a grid, a halftone
   thinning downwards, and a wash of colour - the same devices the marketing
   hero uses, so the two sites read as one company. */
/* The artwork the user chose fills this half. 652x900 and 137KB, so it is
   one request and no larger than the panel it fills; the figure's head sits
   high in the frame, which is why the image is anchored to the top and the
   scrim is weighted to the bottom. */
.auth-art{position:relative;overflow:hidden;background:#080f1d;
  display:flex;align-items:flex-end;padding:3rem}
.auth-grid,.auth-dots,.auth-glow{position:absolute;inset:0;pointer-events:none}
.auth-grid{background-image:url("auth-art.webp");
  background-size:cover;background-position:50% 20%;background-repeat:no-repeat}
/* The scrim. The picture is extremely bright and the wordmark sits on top of
   it; white text straight onto those stripes is unreadable whatever the
   stripe happens to be. Nearly solid where the words are, clearing towards
   the top so the figure still shows - and it keeps the page dark, which is
   what the user asked to hold on to. */
.auth-dots{background:linear-gradient(to top,
    rgba(8,15,29,.97) 0%, rgba(8,15,29,.92) 28%,
    rgba(8,15,29,.62) 55%, rgba(8,15,29,.30) 100%)}
/* A breath of the brand colour over the join, so the panel belongs to this
   site rather than only to the picture. */
.auth-glow{background:
    radial-gradient(ellipse 70% 45% at 20% 100%, rgba(34,211,238,.16), transparent 70%),
    radial-gradient(ellipse 60% 40% at 85% 0%, rgba(8,145,178,.14), transparent 70%);
  animation:auth-drift 26s ease-in-out infinite alternate;will-change:transform}
@keyframes auth-drift{
  0%{transform:translate3d(-2%,-1%,0) scale(1.05)}
  50%{transform:translate3d(2%,1.5%,0) scale(1.16)}
  100%{transform:translate3d(-1%,1%,0) scale(1.08)}}

.auth-art-in{position:relative;z-index:1;max-width:26rem}
.auth-art .mark{font-size:1.5rem;font-weight:700;letter-spacing:.06em;
  color:var(--text-strong);text-decoration:none}
.auth-art .mark span{color:var(--accent)}
.auth-tag{margin:1.1rem 0 1.6rem;color:var(--text);font-size:1.05rem;line-height:1.75}
.auth-points{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.7rem}
.auth-points li{position:relative;padding-left:1.6rem;color:var(--text-muted);
  font-size:.9rem;line-height:1.6}
/* A tick drawn from two borders: no glyph to go missing in a font. */
.auth-points li::before{content:"";position:absolute;left:.15rem;top:.35rem;
  width:.45rem;height:.75rem;border:solid var(--accent);border-width:0 2px 2px 0;
  transform:rotate(45deg)}

/* The form side. */
.auth-side{display:flex;align-items:center;justify-content:center;padding:2.5rem 1.5rem}
.auth-form{width:100%;max-width:23rem}
.auth-form .mark{display:none;font-size:1.3rem;font-weight:700;letter-spacing:.06em;
  color:var(--text-strong);text-decoration:none;margin-bottom:1.5rem}
.auth-form .mark span{color:var(--accent)}
.auth-form h1{font-size:1.75rem;margin:0 0 .4rem}
.auth-form .sub{margin-bottom:1.75rem;font-size:.9rem}
.auth-form form{margin:0}

/* A capsule with its icon inside. The element rules above give every input a
   border and a background; inside a field it hands both to the capsule. */
.auth .field{display:flex;align-items:center;gap:.7rem;height:3.1rem;
  margin-bottom:.9rem;padding:0 1.15rem;border:1px solid var(--line-strong);
  border-radius:999px;background:var(--field);
  transition:border-color .2s ease,box-shadow .2s ease}
.auth .field:focus-within{border-color:var(--accent);
  box-shadow:0 0 0 3px rgba(34,211,238,.14)}
.auth .field .fi{flex:none;display:inline-flex;color:var(--text-muted)}
.auth .field .fi svg{width:1.05rem;height:1.05rem}
.auth .field input{flex:1 1 auto;min-width:0;margin:0;padding:0;height:100%;
  border:0;background:transparent;border-radius:0}
.auth .field input:focus{outline:0;box-shadow:none;border:0}

.auth .check{display:flex;align-items:center;gap:.5rem;margin:.2rem 0 1.4rem;
  color:var(--text-muted);font-size:.875rem}
.auth .check input{width:auto;margin:0}
.auth button[type=submit]{margin-top:0;height:3rem;border-radius:999px;font-size:.95rem}
.auth .foot{margin-top:1.5rem;text-align:left}

/* Hidden from sight, still read aloud. Not display:none, which takes it out
   of the accessibility tree along with everything else. */
.sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}

@media (max-width:900px){
  .auth{grid-template-columns:1fr;min-height:100dvh}
  .auth-art{display:none}
  .auth-form .mark{display:inline-block}
  .auth-side{align-items:flex-start;padding-top:3rem}
}
@media (prefers-reduced-motion:reduce){
  .auth-glow{animation:none}
  .auth .field{transition:none}
}
/* A hairline where the two halves meet, so the join reads as deliberate
   rather than as two backgrounds that happen to differ. */
.auth-art{border-right:1px solid var(--line)}
/* The one control the browser draws itself. accent-color is all it takes to
   bring it into the palette, and it needs no JavaScript and no replacement
   element that keyboards then have to be taught about. */
.auth .check input[type=checkbox]{accent-color:var(--accent);width:1rem;height:1rem}
/* a hint that needs room under it before the next capsule */
.auth .hint.mb{margin:-.4rem 0 .9rem .2rem}
