/* dp-intranet — one stylesheet, no CDN, no inline styles.
   The CSP is style-src 'self', so inline style="" attributes will NOT render. Keep it that way. */

:root {
    --bg:      #f4f5f7;
    --card:    #ffffff;
    --ink:     #16191d;
    --muted:   #666e79;
    --line:    #e2e5ea;
    --accent:  #1f5fd0;
    --ok:      #0d7a4a;
    --danger:  #b3261e;
    --radius:  10px;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ── top bar ─────────────────────────────────────────────── */
.topbar {
    display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
    padding: 12px 24px; background: #fff; border-bottom: 1px solid var(--line);
}
.brand { font-weight: 700; letter-spacing: -0.2px; color: var(--ink); }
.topbar nav { display: flex; gap: 18px; flex: 1; flex-wrap: wrap; }
.topbar nav a { color: var(--muted); padding: 4px 0; border-bottom: 2px solid transparent; }
.topbar nav a.on { color: var(--ink); border-bottom-color: var(--accent); font-weight: 600; }
.signout { display: flex; align-items: center; gap: 10px; margin: 0; }
.who { color: var(--muted); font-size: 13px; }

/* ── layout ──────────────────────────────────────────────── */
.wrap { max-width: 1140px; margin: 0 auto; padding: 28px 24px 64px; }
.wrap.narrow { max-width: 640px; }
h1 { font-size: 26px; margin: 0 0 6px; letter-spacing: -0.3px; }
h2 { font-size: 16px; margin: 0 0 12px; }
h3 { font-size: 15px; margin: 0 0 4px; }
.muted { color: var(--muted); }
.small { font-size: 13px; }
.crumb { margin: 0 0 4px; font-size: 13px; color: var(--muted); }
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.struck { text-decoration: line-through; color: var(--muted); }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); }
.pad { padding: 18px 20px; }
.wrap > .card, .wrap > section { margin-top: 18px; }

.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; margin-top: 18px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; margin-top: 18px; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 18px; margin-top: 18px; }
.stat { display: flex; flex-direction: column; gap: 4px; }
.stat .n { font-size: 26px; font-weight: 700; letter-spacing: -0.5px; }
.stat .l { color: var(--muted); font-size: 13px; }
.stat.overdue .n { color: var(--danger); }

.projcard { display: block; color: inherit; }
.projcard:hover { text-decoration: none; border-color: var(--accent); }
.projcard .desc { color: var(--muted); font-size: 13px; }
.counts { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0 0; }

/* ── lists ───────────────────────────────────────────────── */
.list { list-style: none; margin: 0; padding: 0; }
.list li { padding: 8px 0; border-bottom: 1px solid var(--line); display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; }
.list li:last-child { border-bottom: 0; }
.checks li { align-items: center; }
.tick {
    border: 1px solid var(--line); background: #fff; border-radius: 50%;
    width: 26px; height: 26px; cursor: pointer; color: var(--muted); line-height: 1;
}
.tick:hover { border-color: var(--accent); color: var(--accent); }
.tick.done { color: var(--ok); border-color: var(--ok); }

.tag {
    display: inline-block; padding: 2px 8px; border-radius: 20px;
    background: #eef1f5; color: var(--muted); font-size: 12px; white-space: nowrap;
}
.tag.ok { background: #e6f4ec; color: var(--ok); }
/* Progress ("in progress" derived from task_updates rows, never a stored status value). */
.tag.prog { background: #fdf3e2; color: #92610a; white-space: normal; }
/* due_date < today (SGT, see core/format.js todaySgt()), status still 'todo'. */
.tag.overdue { background: #fbe9e7; color: var(--danger); font-weight: 600; }

/* ── People / per-person page ───────────────────────────────── */
.notewrap { flex: 1 0 100%; margin: 2px 0 0; }
.notewrap input[type=text] { width: auto; flex: 1 1 240px; }

.notice { padding: 10px 0; border-bottom: 1px solid var(--line); }
.notice:last-child { border-bottom: 0; }
.feebadge { background: #eef3fc; border: 1px solid #d6e2f7; border-radius: var(--radius); padding: 10px 14px; }

/* ── tables ──────────────────────────────────────────────── */
.tablewrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); vertical-align: top; }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; }
tbody tr:last-child td { border-bottom: 0; }

/* ── forms ───────────────────────────────────────────────── */
label { display: block; margin: 12px 0 4px; font-size: 13px; color: var(--muted); }
/* ⚠️ EXTEND THIS LIST WHEN YOU INTRODUCE A NEW INPUT TYPE. It is an allow-list of type=
   selectors, so an input whose type is not named here gets NO styling at all and renders as
   a raw browser default - a small unpadded box, obviously wrong beside a full-width button,
   and easy to misread as a layout problem rather than a missing selector. That is exactly
   what happened on 2026-09-05 when the login field became type=tel. */
input[type=text], input[type=password], input[type=number], input[type=file],
input[type=tel], input[type=email], input[type=url], input[type=search],
input[type=date], input[type=datetime-local], input[type=time], select {
    width: 100%; padding: 9px 11px; border: 1px solid var(--line);
    border-radius: 8px; background: #fff; color: var(--ink); font: inherit;
}
input:focus, select:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

form.inline { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; }
form.inline label { margin: 0; }
/* ⚠️ Mirror of the allow-list above. The base rule sets width:100%, which inside an inline
   flex row makes a field take the whole line and wrap the form. Any type added there must be
   answered here, or the page it appears on silently breaks - adding type=date to the base
   list without this would have blown out the "Add an item" row on the project and person
   pages, which is where the only date inputs in the app live. */
form.inline input[type=text], form.inline input[type=number], form.inline input[type=tel],
form.inline input[type=email], form.inline input[type=url], form.inline input[type=search],
form.inline select { width: auto; flex: 1 1 160px; }
/* Date and time pickers are content-sized by the browser and should stay that way - growing
   one to a 160px basis just leaves whitespace inside the control. */
form.inline input[type=date], form.inline input[type=time], form.inline input[type=datetime-local] {
    width: auto; flex: 0 0 auto;
}
form.inline.tight { margin: 0; flex-wrap: nowrap; }

.btn {
    display: inline-block; padding: 9px 15px; border-radius: 8px; cursor: pointer;
    border: 1px solid var(--line); background: #fff; color: var(--ink); font: inherit; white-space: nowrap;
}
.btn:hover { text-decoration: none; border-color: var(--muted); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:hover { background: #1a4fae; }
.btn.small { padding: 5px 10px; font-size: 13px; }
.btn.danger { color: var(--danger); border-color: #f0d5d3; }
.btn.wide { width: 100%; margin-top: 18px; }

/* ── auth pages ──────────────────────────────────────────────────────────────
   Deliberately anonymous: no wordmark, no headings, no labels, no helper text.
   Charles, 2026-09-05: "just a textfield and Continue button ... make it very
   anonymous with minimal instructions." See the comment at the top of login.ejs.

   With every word removed, proportion is the only thing left doing any work, so
   the field and the button share ONE height (--control-h) and one radius and one
   width. They read as a single matched pair. The original complaint was a small
   default-sized field sitting above a full-width button; --control-h is what
   stops that recurring the next time either one is touched.
   ------------------------------------------------------------------------- */
.authpage { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.auth { --control-h: 52px; width: 100%; max-width: 320px; }
/* .auth.pad, not .auth — the card also carries .pad (18px 20px), which is an equally
   specific single-class selector. Relying on source order to win that is the kind of
   silent cancellation that reappears the moment someone reorders the file. */
.auth.pad { padding: 22px; }

/* Height, not padding, so the pair cannot drift apart when a font size changes. */
.auth input[type=tel], .auth input[type=text] {
    height: var(--control-h); padding: 0 14px; border-radius: 10px;
    font-size: 17px; text-align: center;
    /* These two fields hold nothing but digits, so the digits should be even-width. */
    font-variant-numeric: tabular-nums;
}
.auth .btn.wide {
    height: var(--control-h); margin-top: 10px; padding: 0; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    font-size: 16px; font-weight: 600;
}

/* The code is two characters, so it is set large and tracked rather than sitting
   lost in a full-width box. text-indent cancels the trailing letter-space, which
   otherwise pushes centred tracked text visibly off-centre. */
.auth .otp { font-size: 26px; font-weight: 600; letter-spacing: 12px; text-indent: 12px; }

/* Centred to match the field and the button. The base label rule is left-aligned with a
   12px top margin, which is right for the dense forms elsewhere but leaves a one-word
   label floating off to one side of an otherwise symmetrical card. */
.auth label { margin: 0 0 6px; text-align: center; font-size: 12px; letter-spacing: 0.6px; }

/* The masked number the person just typed, echoed back. Same size and colour as the label
   directly beneath it, so the two read as one quiet block of context rather than two
   competing lines - and so the label is not the smaller of the pair, which inverted the
   hierarchy: what to type matters more than where it went. */
.sentto { margin: 0 0 4px; text-align: center; font-size: 12px; letter-spacing: 0.6px; color: var(--muted); }
.backlink { margin: 14px 0 0; text-align: center; font-size: 13px; }
.backlink a { color: var(--muted); }
.backlink a:hover { color: var(--accent); }

.auth .alert { margin: 0 0 14px; }
.alert {
    background: #fdecea; border: 1px solid #f5c6c2; color: var(--danger);
    padding: 9px 12px; border-radius: 8px; font-size: 14px;
}
.code { font-size: 44px; margin-bottom: 4px; }

/* ── WIP Files: reference numbers + paste-upload feedback ── */
.fileno {
    font: 600 13px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    color: var(--accent); background: #eef3fc; border: 1px solid #d6e2f7;
    border-radius: 6px; padding: 4px 7px; white-space: nowrap;
}
button.fileno { cursor: pointer; }
button.fileno:hover { background: #dfe9fa; border-color: var(--accent); }
kbd {
    font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    background: #fff; border: 1px solid var(--line); border-bottom-width: 2px;
    border-radius: 4px; padding: 2px 5px; color: var(--ink);
}
.flash {
    margin: 10px 0 0; padding: 9px 12px; border-radius: 8px; font-size: 14px;
    background: #e8f5ee; border: 1px solid #c3e4d2; color: var(--ok);
}
.flash.bad { background: #fdecea; border-color: #f5c6c2; color: var(--danger); }

/* Upload progress. A native <progress> rather than a styled div: its width is a property, not
   a style attribute, so nothing here fights the style-src 'self' CSP — and dropping the value
   attribute gives the browser's own indeterminate animation for free, which is what shows
   while the server is saving and there is no number left to report. */
.uploadprogress { margin-top: 14px; }
.uploadprogress p { margin: 6px 0 0; }
/* The project Documents form is a flex row (form.inline) — without this the bar would try to
   sit as another control beside the Upload button instead of on its own line under it. */
form.inline .uploadprogress { flex: 1 0 100%; margin-top: 4px; }
.uploadstatus { margin-bottom: 10px; }
.uprog {
    display: block; width: 100%; height: 8px; border: 0; border-radius: 999px;
    -webkit-appearance: none; -moz-appearance: none; appearance: none;
    background: #e6e9ef; color: var(--accent);   /* colour is the IE/legacy fill */
    overflow: hidden;
}
.uprog::-webkit-progress-bar { background: #e6e9ef; border-radius: 999px; }
.uprog::-webkit-progress-value { background: var(--accent); border-radius: 999px; transition: width .15s linear; }
.uprog::-moz-progress-bar { background: var(--accent); border-radius: 999px; }
/* Indeterminate (no value attribute) in Chrome/Android WebView has no ::-webkit-progress-value
   to paint, so without this it would sit as an empty grey track exactly when it most needs to
   look alive. */
.uprog:indeterminate {
    background: repeating-linear-gradient(-45deg, #cfdcf5 0 12px, #e6e9ef 12px 24px);
    background-size: 34px 100%;
    animation: uprog-slide .7s linear infinite;
}
.uprog:indeterminate::-webkit-progress-bar { background: none; }
@keyframes uprog-slide { to { background-position: 34px 0; } }
/* Copy fallback target for browsers without navigator.clipboard — it must be selectable,
   so it is moved out of sight rather than hidden. */
.offscreen { position: fixed; top: -1000px; left: -1000px; opacity: 0; }

/* ── View As banner ──────────────────────────────────────────── */
.viewas-banner {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    padding: 8px 24px; background: #2a2210; color: #ffe9a8;
    border-bottom: 1px solid #4a3c17; font-size: 13px;
}
.viewas-banner strong { color: #fff; }
.viewas-banner form { margin: 0 0 0 auto; }
.viewas-banner .btn { background: #ffe9a8; border-color: #ffe9a8; color: #2a2210; }
.viewas-banner .btn:hover { background: #fff; border-color: #fff; }
.viewas-trigger { margin: 10px 0 0; }

@media (max-width: 700px) {
    /* Two rows: brand + sign-out on top, the nav as one horizontal scrolling strip below.
       Left to itself the flex nav wraps one link per line and eats half the screen. */
    .topbar { gap: 10px; padding: 10px 16px; row-gap: 8px; }
    .brand { flex: 1 1 auto; }
    .signout { order: 2; }
    .topbar nav {
        order: 3; flex: 1 0 100%; gap: 16px;
        flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch;
    }
    .topbar nav a { white-space: nowrap; }
    .wrap { padding: 20px 16px 48px; }
    form.inline { flex-direction: column; align-items: stretch; }
    form.inline input, form.inline select, form.inline .btn { width: 100%; }
    form.inline.tight { flex-direction: row; }
    .viewas-banner { padding: 8px 16px; }
    .viewas-banner form { margin-left: 0; }
}
