/* ===========================================================================
   Confidential reporting portal (/report*) — additions to auth.css.

   The portal shares the sign-in screen's art direction ("Control Room /
   Signal Terminal") rather than carrying its own: one palette, one set of
   faces, one field treatment, so the two screens cannot drift apart. This
   file holds only what the portal has and the sign-in screen does not — the
   clarification block on the stage, the tabbed entry panel, and the content
   surfaces the reporting screens need.

   /!\ EVERY CLASS HERE IS PREFIXED pt-. The application layout ships Tailwind
       inline, so a bare class name silently inherits a utility: `.ring` once
       painted a 3px blue box-shadow on the Leave Summary gauges with nothing
       in the source to explain it. `ring`, `grid`, `table`, `border`,
       `shadow`, `outline`, `filter`, `transform`, `container`, `block`,
       `hidden`, `fixed`, `static` and `truncate` are all live utilities.

   /!\ EVERY @media BLOCK IS AT THE BOTTOM OF THIS FILE. A responsive block
       adds no specificity, so one placed above the rules it overrides is an
       exact tie that loses on source order — it lints clean, renders, and
       simply does nothing. That has happened twice in this codebase
       (department.css, twice in one day) and the only symptom was a tile
       overflowing at one width.

   The one exception to the pt- rule is `.hp`, the honeypot, which keeps its
   name because the field name and the class are part of a shape scripts have
   already been trained on.
   =========================================================================== */

/* =========================================================================
   DISPLAY MODES
   =========================================================================
   Three, chosen 2026-09-18 because the portal was dark-only and not everybody
   can read that comfortably: `control` (the original dark control room, and
   still the default), `daylight` (warm paper) and `slate` (cool grey).

   /!\ auth.css IS NOT TOUCHED AND MUST NOT BE. It is shared with /login, so
       re-theming it there would change the STAFF SIGN-IN SCREEN. Everything
       below redefines the same custom property names on `.pt`, which is portal
       only and beats auth.css's `.auth` on specificity.

   /!\ THE LAYOUT DOES NOT CHANGE BETWEEN MODES -- only the palette. Same
       blocks, same order, same spacing, same type scale, same breakpoints.
       A mode that also moves things is three screens to design and three to
       regress, and somebody who switches loses their place.

   /!\ AMBER MEANS "THIS NEEDS YOUR ATTENTION", IN ALL THREE. It is the one
       colour that survives a mode change unchanged in MEANING -- though not in
       value: #ffb000 is ~1.9:1 on white and unreadable, so the light modes use
       a darker amber of the same family. If amber appears on a screen where
       nothing needs the reader, that is a bug, not a decoration.

   /!\ THE MODE IS STAMPED SERVER-SIDE ON <body>, never by script. A theme
       applied after first paint flashes dark -- which is exactly the wrong
       thing to do to the person who chose light because dark is hard for them
       to read.
   ========================================================================= */

/* ---- control: the original. Repeated here rather than inherited so all three
        modes read as one table, and so a change to one is visibly a change to
        one. ---- */
.pt {
    --signal-rgb: 255 176 0;
    --constellation-rgb: 220 226 235;
    --ink:      #0a0d12;
    --ink-2:    #0e121a;
    --ink-3:    #141a24;
    /* A field is RAISED out of the card on ink and RECESSED into it on paper.
       Same idea either way: a control must not be the same tone as the surface
       it sits on. See the .control override further down. */
    --field:        #141a24;
    --field-focus:  #171b22;
    --line:     rgba(255, 255, 255, 0.08);
    --line-2:   rgba(255, 255, 255, 0.14);
    --text:     #e9ebef;
    --muted:    #828c9b;
    --faint:    #4b5563;
    --signal:   #ffb000;
    --signal-2: #ffc94d;
    --signal-soft: rgb(var(--signal-rgb) / 0.13);
    --ok:       #4ade80;

    /* Portal-only tokens. These were hardcoded dark values scattered through
       this file; a mode cannot exist until they are named. */
    --hover:          rgba(255, 255, 255, 0.03);
    --on-signal:      #1a1205;   /* text ON an amber fill */
    --grain:          0.05;      /* film-grain opacity */
    --note-info-bg:   #241c0c;   --note-info-fg:   var(--signal-2);
    --note-err-line:  #ff6b6b;   --note-err-bg:    #251416;
    --note-err-fg:    #ffc4c4;   --note-err-strong:#ffd5d5;
    --note-ok-bg:     #11241a;   --note-ok-fg:     #bdf0cf;
    --note-ok-strong: #d7f7e2;
    --pill-ok-bg:     #11241a;   --pill-ok-fg:     #bdf0cf;
    --pill-ok-line:   rgba(74, 222, 128, 0.3);
    --tile-ok-fg:     #7ee0a1;
}

/* ---- daylight: warm paper ---- */
.pt[data-pt-theme="daylight"] {
    --signal-rgb: 167 95 0;
    --constellation-rgb: 92 80 62;
    --ink:      #faf7f1;
    --ink-2:    #f3ede3;
    --ink-3:    #fffdf9;
    --field:        #f3ede3;
    --field-focus:  #fffdf9;
    --line:     rgba(62, 47, 26, 0.12);
    --line-2:   rgba(62, 47, 26, 0.22);
    --text:     #1e1913;
    --muted:    #6b6053;
    --faint:    #7d7364;
    /* Darker amber of the same family: #ffb000 on paper is ~1.9:1. */
    --signal:   #a75f00;
    --signal-2: #7d4600;
    --signal-soft: rgba(167, 95, 0, 0.11);
    --ok:       #1a7a45;

    --hover:          rgba(62, 47, 26, 0.045);
    --on-signal:      #fffdf9;
    --grain:          0;          /* grain on paper reads as dirt */
    --note-info-bg:   #fdf3e0;   --note-info-fg:   #6d3f00;
    --note-err-line:  #c0342b;   --note-err-bg:    #fdeceb;
    --note-err-fg:    #8c1f18;   --note-err-strong:#6d1712;
    --note-ok-bg:     #e9f6ee;   --note-ok-fg:     #146036;
    --note-ok-strong: #0d4527;
    --pill-ok-bg:     #e9f6ee;   --pill-ok-fg:     #146036;
    --pill-ok-line:   rgba(26, 122, 69, 0.28);
    --tile-ok-fg:     #146036;
}

/* ---- slate: cool grey ---- */
.pt[data-pt-theme="slate"] {
    --signal-rgb: 157 90 0;
    --constellation-rgb: 74 84 102;
    --ink:      #f4f6f9;
    --ink-2:    #eaeef4;
    --ink-3:    #ffffff;
    --field:        #eaeef4;
    --field-focus:  #ffffff;
    --line:     rgba(19, 30, 48, 0.12);
    --line-2:   rgba(19, 30, 48, 0.22);
    --text:     #101725;
    --muted:    #57637a;
    --faint:    #6d7888;
    --signal:   #9d5a00;
    --signal-2: #744100;
    --signal-soft: rgba(157, 90, 0, 0.10);
    --ok:       #15704a;

    --hover:          rgba(19, 30, 48, 0.045);
    --on-signal:      #ffffff;
    --grain:          0;
    --note-info-bg:   #f6efe3;   --note-info-fg:   #6a3d00;
    --note-err-line:  #b93330;   --note-err-bg:    #fceceb;
    --note-err-fg:    #86211e;   --note-err-strong:#671715;
    --note-ok-bg:     #e6f3ec;   --note-ok-fg:     #115a3b;
    --note-ok-strong: #0b4029;
    --pill-ok-bg:     #e6f3ec;   --pill-ok-fg:     #115a3b;
    --pill-ok-line:   rgba(21, 112, 74, 0.28);
    --tile-ok-fg:     #115a3b;
}

/* The film grain is atmospheric on ink and visible dirt on paper, so its
   opacity is a token rather than a constant. auth.css sets 0.05 directly;
   this overrides it for the portal only. */
.pt .auth-wrap::after { opacity: var(--grain); }

/* On a light ground the selection has to invert too, or selected text is amber
   on amber. */
.pt ::selection { background: var(--signal); color: var(--on-signal); }

/* =======================  STAGE — the clarification  ===================== */

/* The stage carries real content here, unlike the sign-in screen where it is
   atmospheric and short, so on a laptop it can run past the fold.
   /!\ It is deliberately NOT given `overflow-y: auto`. That was tried and is
       worse: a clarification inside its own scroller is one people do not
       finish reading, because nothing about the page suggests there is more.
       Letting the PAGE scroll keeps the whole thing reachable by the gesture
       everyone already makes. Measured at 1440x900 the overflow is 29px. */

/* The sign-in headline is clamp(3.2rem, 7vw, 6.4rem) — correct when it is the
   only thing on the stage, far too large above four promises and a warning. */
/* The measure is set ONCE, on the parent. Setting it on each child in `ch`
   made the warning card 346px against the promise list's 412px — the two use
   different font sizes, so the same `ch` value is a different width and the
   blocks did not line up. */
.pt-stage-full .stage-head { max-width: 27rem; }
.pt-stage-full .headline { font-size: clamp(2.3rem, 4.2vw, 3.5rem); }
.pt-stage-full .kicker { margin-bottom: 1rem; }
.pt-stage-brief .stage-head { max-width: 22ch; }

/* The status chip's dot is green (--ok) on the sign-in screen, where it means
   "all systems nominal". Here the chip states a caveat, not a health check —
   amber, in the same signal colour as the warning it introduces. */
.pt-chip .dot {
    background: var(--signal);
    box-shadow: 0 0 0 0 rgb(var(--signal-rgb) / 0.6);
    animation: pt-pulse 2.6s infinite;
}
@keyframes pt-pulse {
    0%   { box-shadow: 0 0 0 0 rgb(var(--signal-rgb) / 0.5); }
    70%  { box-shadow: 0 0 0 7px rgb(var(--signal-rgb) / 0); }
    100% { box-shadow: 0 0 0 0 rgb(var(--signal-rgb) / 0); }
}

/* The brand row carries a longer chip here than the sign-in screen's "all
   systems nominal", so let it wrap onto two lines rather than break both
   labels mid-phrase. */
.pt .stage-top { flex-wrap: wrap; row-gap: 0.6rem; }
.pt-chip { white-space: nowrap; }

/* The four promises. Each row is marked with a small amber node that echoes
   the constellation behind it, rather than a bullet. */
.pt-promise {
    list-style: none;
    margin: 1.9rem 0 0;
    padding: 0;
}
.pt-promise li {
    position: relative;
    padding: 0.62rem 0 0.62rem 1.6rem;
    font-size: 0.9rem;
    line-height: 1.55;
    color: var(--muted);
    border-top: 1px solid var(--line);
}
.pt-promise li:first-child { border-top: 0; padding-top: 0; }
.pt-promise li strong { color: var(--text); font-weight: 600; }
.pt-promise li::before {
    content: "";
    position: absolute;
    left: 1px;
    top: 1.28em;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--signal);
    box-shadow: 0 0 0 3px var(--signal-soft);
}
.pt-promise li:first-child::before { top: 0.62em; }

/* The honest warning. Amber wash rather than a filled panel — it is a caveat
   to read, not an error that has occurred. */
.pt-warn {
    margin-top: 1.6rem;
    padding: 0.95rem 1.05rem 1rem;
    border: 1px solid rgb(var(--signal-rgb) / 0.24);
    border-left: 2px solid var(--signal);
    border-radius: 10px;
    background: linear-gradient(90deg, var(--signal-soft), rgb(var(--signal-rgb) / 0.03) 70%, transparent);
    font-size: 0.84rem;
    line-height: 1.55;
    color: var(--muted);
}
.pt-warn-head {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--signal); margin: 0 0 0.55rem;
}
.pt-warn p { margin: 0 0 0.55rem; }
.pt-warn p:last-child { margin-bottom: 0; }
.pt-warn strong { color: var(--signal-2); font-weight: 600; }
.pt-warn ul { margin: 0.5rem 0 0; padding-left: 1.05rem; }
.pt-warn li { margin-bottom: 0.35rem; }
.pt-warn li:last-child { margin-bottom: 0; }

/* =======================  PANEL  ========================================= */

/* auth.css caps .panel-inner at 380px — right for a sign-in column, too narrow
   for the case form or a message thread.
   /!\ Raising that cap alone does NOTHING, which is how this was first written.
       auth.css splits .auth-wrap 1.15fr / 0.85fr, so at a 1060px viewport the
       panel column is 450px and its inner was measured at 359 — the 620px cap
       could never bind. The COLUMN has to give way too. On these screens the
       stage carries only a kicker and a two-word headline, so it is the half
       that can afford to. */
.pt-wide .auth-wrap { grid-template-columns: 0.78fr 1.22fr; }
.pt-wide .pt-panel-inner { max-width: 620px; }

/* These two screens scroll — the case form runs past 1700px. A grid item
   stretches to the whole document, so the stage was 1763px tall with
   `justify-content: space-between` pushing its headline into the middle of
   nowhere and its footer to the very bottom: by the time you were typing, the
   left half was blank. Pinning it keeps the reference and the status in view
   for the whole scroll, which on the case thread is the point of having it.
   The LANDING page deliberately does not do this — there the stage carries the
   clarification, which has to scroll away like ordinary reading matter. */
.pt-wide .stage {
    position: sticky;
    top: 0;
    height: 100vh;
    align-self: start;
}

.pt-back {
    display: inline-flex; align-items: center; gap: 0.45rem;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--muted); text-decoration: none; margin-bottom: 1.4rem;
    transition: color var(--motion-duration);
}
.pt-back:hover { color: var(--signal); }

/* =======================  TABBED ENTRY  ================================== */

/* Three ways in. Without JS the tab strip never appears, every panel renders
   under its own heading and all three forms are usable — the .pt-js class is
   set in <head> before first paint, so the tabbed state does not flash. */
.pt-tabs {
    display: none;
    position: relative;
    gap: 0;
    margin: 0 0 1.7rem;
    border-bottom: 1px solid var(--line-2);
}
.pt-js .pt-tabs { display: flex; }
.pt-tab {
    flex: 1;
    background: none; border: 0; cursor: pointer;
    padding: 0.6rem 0.25rem 0.75rem;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.6rem; letter-spacing: 0.15em; text-transform: uppercase;
    color: var(--muted);
    transition: color var(--motion-duration);
}
.pt-tab:hover { color: var(--text); }
.pt-tab[aria-selected="true"] { color: var(--signal); }
.pt-tab:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; border-radius: 6px; }

/* The moving underline. Positioned by the inline controller in start.php via
   two custom properties, so there is no layout maths in CSS and no second
   element to keep in sync when a tab is added. */
.pt-ink {
    position: absolute;
    bottom: -1px; left: 0;
    height: 2px;
    width: var(--pt-ink-w, 0);
    background: var(--signal);
    transform: translateX(var(--pt-ink-x, 0));
    transition: transform 380ms cubic-bezier(0.22, 1, 0.36, 1),
                width 380ms cubic-bezier(0.22, 1, 0.36, 1);
    box-shadow: 0 0 12px rgb(var(--signal-rgb) / 0.5);
}

/* Each panel's own heading — the no-JS wayfinding, removed once tabs exist. */
.pt-panel-head {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400; font-size: 1.5rem;
    margin: 2.2rem 0 0.4rem;
}
.pt-panel:first-child .pt-panel-head { margin-top: 0; }
.pt-js .pt-panel-head { display: none; }
.pt-panel-sub { color: var(--muted); font-size: 0.88rem; margin: 0 0 1.4rem; line-height: 1.55; }

/* =======================  CONTENT SURFACES  ============================== */

.pt-card {
    padding: 1.05rem 1.15rem 1.15rem;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--ink-3);
    margin-bottom: 1.1rem;
}
.pt-card:last-child { margin-bottom: 0; }
.pt-card-head {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--muted); margin: 0 0 0.8rem;
}

/* Notes. Not .auth-flash — that carries a dismiss button and a countdown bar,
   which are wrong for a statement that belongs to the page rather than to a
   single request. */
.pt-note {
    padding: 0.8rem 0.95rem;
    border: 1px solid var(--line-2);
    border-left-width: 2px;
    border-radius: 10px;
    font-size: 0.86rem; line-height: 1.5;
    margin-bottom: 1.3rem;
    background: var(--ink-3);
    color: var(--muted);
}
.pt-note > :first-child { margin-top: 0; }
.pt-note > :last-child { margin-bottom: 0; }
.pt-note strong { color: var(--text); font-weight: 600; }
.pt-note-info { border-left-color: var(--signal); background: var(--note-info-bg); color: var(--note-info-fg); }
.pt-note-info strong { color: var(--signal-2); }
.pt-note-warn { border-left-color: var(--signal); background: rgb(var(--signal-rgb) / 0.07); color: var(--signal-2); }
.pt-note-warn strong { color: var(--signal-2); }
.pt-note-err  { border-left-color: var(--note-err-line); background: var(--note-err-bg); color: var(--note-err-fg); }
.pt-note-err strong { color: var(--note-err-strong); }
.pt-note-ok   { border-left-color: var(--ok); background: var(--note-ok-bg); color: var(--note-ok-fg); }
.pt-note-ok strong { color: var(--note-ok-strong); }
.pt-note ul { margin: 0.5rem 0 0; padding-left: 1.05rem; }
.pt-note li { margin-bottom: 0.3rem; }

/* The case reference. The one piece of data on this portal somebody has to
   copy by hand, so it is set in the mono face at the largest size here. */
.pt-ref {
    display: inline-block;
    font-family: "IBM Plex Mono", monospace;
    font-size: 1.35rem; font-weight: 600; letter-spacing: 0.1em;
    color: var(--signal);
    padding: 0.7rem 1.1rem;
    border: 1px dashed rgb(var(--signal-rgb) / 0.4);
    border-radius: 10px;
    background: var(--signal-soft);
}

/* The 6-digit code field, inside the shared .control frame. */
.control input.pt-code {
    font-family: "IBM Plex Mono", monospace;
    font-size: 1.4rem; font-weight: 600;
    letter-spacing: 0.5em; text-indent: 0.5em;
    text-align: center;
    padding-top: 0.7rem; padding-bottom: 0.7rem;
}
.control input.pt-mono {
    font-family: "IBM Plex Mono", monospace;
    letter-spacing: 0.08em;
}

/* /!\ NEUTRAL, AND IT USED TO BE AMBER. A status is INFORMATION -- "Received",
       "In progress", "Concluded" ask nothing of the reader -- and on a register
       of twenty reports an amber pill on every row is precisely amber as
       decoration, which is the one thing the palette rule forbids. Amber is
       left to .pt-pill-signal, which says "Needs you" and means it.
       Found by measuring the rendered colours rather than by eye: every pill on
       the page came back rgb(167 95 0) in daylight. */
.pt-pill {
    display: inline-block;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
    padding: 0.35rem 0.7rem; border-radius: 999px;
    color: var(--muted); background: var(--ink-3);
    border: 1px solid var(--line-2);
}
.pt-pill-ok { color: var(--pill-ok-fg); background: var(--pill-ok-bg); border-color: var(--pill-ok-line); }

/* Summary key/value table. */
.pt-kv { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.pt-kv td { padding: 0.6rem 0; border-top: 1px solid var(--line); vertical-align: top; }
.pt-kv tr:first-child td { border-top: 0; padding-top: 0; }
.pt-kv td:first-child {
    width: 38%; color: var(--muted);
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.64rem; letter-spacing: 0.12em; text-transform: uppercase;
    padding-right: 1rem; padding-top: 0.72rem;
}
.pt-kv tr:first-child td:first-child { padding-top: 0.12rem; }
.pt-kv td:last-child { color: var(--text); }

/* The message thread. Reporter and review team are the only two voices — no
   auditor names, ever (MD-007 section 5.3). */
.pt-msg {
    border: 1px solid var(--line);
    border-radius: 11px;
    padding: 0.8rem 0.95rem;
    margin-bottom: 0.7rem;
    background: var(--ink-2);
}
.pt-msg:last-child { margin-bottom: 0; }
.pt-msg-them { background: var(--ink-3); border-color: var(--line-2); }
.pt-msg-req  { background: rgb(var(--signal-rgb) / 0.07); border-color: rgb(var(--signal-rgb) / 0.3); }
.pt-msg-who {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.58rem; letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--faint); margin-bottom: 0.45rem;
}
.pt-msg-req .pt-msg-who { color: var(--signal); }
.pt-msg-body { white-space: pre-wrap; font-size: 0.89rem; line-height: 1.55; color: var(--text); }

/* Evidence receipt list. */
.pt-files { list-style: none; margin: 0; padding: 0; font-size: 0.85rem; }
.pt-files li {
    display: flex; justify-content: space-between; gap: 1rem;
    padding: 0.55rem 0; border-top: 1px solid var(--line);
    color: var(--text);
}
.pt-files li:first-child { border-top: 0; padding-top: 0; }
.pt-files .pt-file-meta { color: var(--faint); font-size: 0.76rem; white-space: nowrap; }
.pt-files .pt-file-scan { color: var(--signal); font-size: 0.7rem; font-family: "IBM Plex Mono", monospace; }

/* "What happens now" — the promise list, inside the panel this time. */
.pt-list { list-style: none; margin: 0.3rem 0 0; padding: 0; }
.pt-list li {
    position: relative; padding: 0.5rem 0 0.5rem 1.4rem;
    font-size: 0.87rem; line-height: 1.5; color: var(--muted);
    border-top: 1px solid var(--line);
}
.pt-list li:first-child { border-top: 0; }
.pt-list li::before {
    content: ""; position: absolute; left: 2px; top: 1.15em;
    width: 5px; height: 5px; border-radius: 50%; background: var(--signal); opacity: 0.75;
}
.pt-list li:first-child::before { top: 0.95em; }

/* Action rows. .btn-signal is full-width by design, so a row of actions needs
   its own quieter partner rather than a second gold block. */
.pt-actions {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem;
    margin-top: 1.4rem;
}
.pt-actions .btn-signal { width: auto; flex: 1 1 auto; min-width: 12rem; }
.pt-btn-quiet {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--muted); background: none;
    border: 1px solid var(--line-2); border-radius: 10px;
    padding: 0.85rem 1.1rem; cursor: pointer; text-decoration: none;
    display: inline-flex; align-items: center; justify-content: center;
    transition: color var(--motion-duration), border-color var(--motion-duration);
}
.pt-btn-quiet:hover { color: var(--text); border-color: var(--muted); }

/* A link that is genuinely a convenience, not a control — "close this page
   now". Deliberately not a button: the open case expires on its own, and the
   people most at risk on a shared machine are the least likely to press it. */
.pt-exit {
    display: flex; align-items: center; justify-content: center;
    flex-wrap: wrap; gap: 0.6rem;
    margin-top: 1.6rem; padding-top: 1.2rem;
    border-top: 1px solid var(--line);
    font-size: 0.76rem; color: var(--faint);
}
.pt-linkish {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; color: var(--signal); text-decoration: underline;
    text-underline-offset: 3px;
}
.pt-linkish:hover { color: var(--signal-2); }

.pt-foot {
    margin-top: 1.8rem; padding-top: 1.1rem;
    border-top: 1px solid var(--line);
    text-align: center;
    font-size: 0.78rem; color: var(--faint); line-height: 1.6;
}
.pt-foot a { color: var(--muted); text-decoration: none; font-weight: 600; }
.pt-foot a:hover { color: var(--signal); text-decoration: underline; text-underline-offset: 3px; }

/* Honeypot: hidden from people, irresistible to scripts. Deliberately NOT
   display:none — some bots skip those; off-screen is more convincing. */
.hp { position: absolute; left: -9999px; top: -9999px; height: 0; overflow: hidden; }

/* =======================  SIGNED-IN SHELL  ==============================
   The portal's own application layout: a sticky bar and ONE content column.
   Same palette, same faces, same field treatment as the entry screens -- it is
   the same portal, not a second one.

   /!\ THE 232px SIDEBAR IS GONE (2026-09-19, the user's call). There is one
       screen now, so a rail of links to the page you are already on was a
       column spent saying nothing. What lived in it moved into the bar: the
       action, and a menu behind the account circle.

   /!\ This is NOT layouts/app.php and must never become it. That shell loads
       ShellService, the menu, the permission resolver, notifications,
       favourites, Alpine, Tailwind and the theme system, every one of which
       reads an authenticated session that does not exist on these paths. See
       _head.php's docblock for the rest of the reasoning.
   ======================================================================== */

.pt-app-wrap { min-height: 100vh; }

/* ---- the bar ---- */
.pt-bar {
    position: sticky; top: 0; z-index: 30;
    display: flex; align-items: center; gap: 1rem;
    padding: 0 1.4rem; height: 56px;
    /* /!\ OPAQUE, and it has to stay so: the page scrolls under this. Every
           surface token in this portal is solid, which is what makes that
           safe -- auth.css's translucent card tokens are not used here. */
    background: var(--ink-2);
    border-bottom: 1px solid var(--line);
}

.pt-bar-brand {
    display: flex; align-items: center; gap: 0.6rem;
    text-decoration: none; color: var(--text); margin-right: auto; min-width: 0;
}
.pt-bar-brand .glyph { color: var(--signal); display: flex; flex: 0 0 auto; }
.pt-bar-brand-txt {
    display: flex; flex-direction: column; gap: 0.1rem;
    font-weight: 650; font-size: 0.82rem; line-height: 1.15; min-width: 0;
}
.pt-bar-brand-txt em {
    font-style: normal;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.52rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--faint);
}

.pt-bar-right { display: flex; align-items: center; gap: 0.7rem; flex: 0 0 auto; }

/* The one action on the portal, and the only amber on a calm screen. */
.pt-bar-new.btn-signal {
    width: auto; min-width: 0;
    padding: 0.5rem 1rem; height: auto;
    display: inline-flex; align-items: center; gap: 0.45rem;
    font-size: 0.78rem;
}
.pt-bar-new-plus { font-size: 1rem; line-height: 1; font-weight: 400; }

/* ---- the circle, and what is behind it ---- */
.pt-acct-menu { position: relative; }
/* A <details> marker is a triangle nobody asked for. */
.pt-acct-menu > summary { list-style: none; }
.pt-acct-menu > summary::-webkit-details-marker { display: none; }

.pt-acct-btn {
    position: relative; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; border-radius: 999px;
    border: 1px solid var(--line-2); background: var(--ink-3);
    transition: border-color var(--motion-duration);
}
.pt-acct-btn:hover { border-color: var(--signal); }
.pt-acct-menu[open] > .pt-acct-btn { border-color: var(--signal); }
.pt-acct-btn:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.pt-acct-chip {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.72rem; font-weight: 600; color: var(--text); line-height: 1;
}

/* /!\ A DOT, NOT A COUNT. The number lives on the Needs-you block, and one
       number written in two places is how the two eventually disagree. */
.pt-acct-dot {
    position: absolute; top: -1px; right: -1px;
    width: 9px; height: 9px; border-radius: 999px;
    background: var(--signal); border: 2px solid var(--ink-2);
}

.pt-menu {
    position: absolute; top: calc(100% + 0.55rem); right: 0; z-index: 40;
    width: 17rem; padding: 0.5rem;
    background: var(--ink-2);
    border: 1px solid var(--line-2); border-radius: 12px;
    box-shadow: 0 14px 40px rgb(0 0 0 / 0.35);
}
.pt-menu-who {
    padding: 0.5rem 0.6rem 0.7rem;
    display: flex; flex-direction: column; gap: 0.15rem; min-width: 0;
}
.pt-menu-name { font-size: 0.86rem; font-weight: 600; color: var(--text); }
.pt-menu-mail {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.66rem; color: var(--faint); overflow-wrap: anywhere;
}
.pt-menu-sep { height: 1px; background: var(--line); margin: 0.35rem 0.1rem; }

.pt-menu-item {
    display: flex; align-items: center; gap: 0.6rem; width: 100%;
    padding: 0.55rem 0.6rem; border-radius: 8px;
    background: none; border: 0; cursor: pointer; text-align: left;
    text-decoration: none; color: var(--muted);
    font-family: inherit; font-size: 0.84rem;
    transition: color var(--motion-duration), background var(--motion-duration);
}
.pt-menu-item:hover { color: var(--text); background: var(--hover); }
.pt-menu-ico { display: flex; flex: 0 0 auto; opacity: 0.75; }
.pt-menu-danger:hover { color: var(--signal); }
.pt-menu-out { margin: 0; }

/* The sentence that used to sit at the bottom of an open report. */
.pt-menu-note {
    margin: 0.2rem 0.6rem 0.6rem;
    font-size: 0.7rem; line-height: 1.45; color: var(--faint);
}

/* The mode switcher, inside the menu rather than on a sidebar foot. */
.pt-menu .pt-modes { margin: 0; padding: 0.35rem 0.6rem 0.55rem; }

/* ---- the content column ---- */
.pt-main { min-width: 0; padding: 1.5rem 2rem 3rem; }
/* 760px is a measure for a form or a record: one column of prose and fields.
   /!\ THE HOME PAGE IS NOT THAT SHAPE and asks for `roomy` -- a register with a
       rail beside it comes out 450 and 291 at this width, which is narrower
       than either column's content. */
.pt-main-inner { max-width: 760px; margin: 0 auto; }
.pt-roomy .pt-main-inner { max-width: 1180px; }

/* =======================  THE PAGE HEADER  ==============================
   /!\ .pt-ph IS COMPACT AND MUST STAY COMPACT. The home page opened with an
       eyebrow, a viewport-scaled serif title and a sub-line -- about 200px of
       introduction, on a screen whose job is to show somebody the state of
       their reports. Measured before it was changed: the first case row started
       732px down an 860px viewport. The serif stays, because it is this
       portal's voice, at a FIXED size; `clamp(1.9rem, 3.4vw, 2.6rem)` is what
       made a wide screen worse rather than better.
   ======================================================================== */
.pt-ph {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.2rem 0.75rem;
    padding-bottom: 0.7rem; margin-bottom: 1.1rem;
    border-bottom: 1px solid var(--line);
}
.pt-ph-title {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400; font-size: 1.45rem; line-height: 1.15;
    margin: 0; color: var(--text);
}
.pt-ph-sub { margin: 0; color: var(--muted); font-size: 0.82rem; line-height: 1.4; }

/* The older header, still used by the composer, the record and the profile --
   screens that are one column of prose and want room to breathe. */
.pt-head { margin-bottom: 1.8rem; }
.pt-eyebrow {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.6rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--faint); margin: 0 0 0.6rem;
}
.pt-title {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400; font-size: clamp(1.9rem, 3.4vw, 2.6rem);
    line-height: 1.08; margin: 0 0 0.5rem; color: var(--text);
}
.pt-sub { margin: 0; color: var(--muted); font-size: 0.9rem; line-height: 1.55; }

/* /!\ THE KPI TILES AND THE OLD REPORT ROWS WENT WITH THE TWO SCREENS THEY
       BELONGED TO (2026-09-19). The counts are the flow strip now and the rows
       are the register; both are further down this file. What is kept from
       that block is the one rule the register still needs. */
.pt-pill-signal {
    color: var(--signal-2);
    background: rgb(var(--signal-rgb) / 0.12);
    border-color: rgb(var(--signal-rgb) / 0.35);
}

/* ---- empty state ----
   An empty portal is the NORMAL first view for a new account, so this is a real
   screen rather than a blank one: five zero tiles read as a page that failed to
   load, which is the worst possible first impression for a channel asking to be
   trusted. */
.pt-empty {
    border: 1px dashed var(--line-2); border-radius: 14px;
    padding: 2.6rem 1.6rem; text-align: center;
    background: var(--ink-2);
}
.pt-empty-mark { display: inline-flex; color: var(--signal); opacity: 0.55; margin-bottom: 0.9rem; }
.pt-empty h2 {
    font-family: "Instrument Serif", Georgia, serif;
    font-weight: 400; font-size: 1.35rem; margin: 0 0 0.5rem; color: var(--text);
}
.pt-empty p {
    margin: 0 auto 1.5rem; max-width: 34rem;
    color: var(--muted); font-size: 0.88rem; line-height: 1.6;
}
.pt-empty .btn-signal { width: auto; min-width: 15rem; margin: 0 auto; }

/* The signed-in shell's cards need a little more room than the entry panel's. */
.pt-app .pt-card { padding: 1.2rem 1.3rem; }
.pt-app .pt-note { margin-bottom: 0.9rem; }
.pt-app .pt-note a { color: var(--signal); text-decoration: underline; text-underline-offset: 3px; }

/* .btn-signal as a LINK.
   /!\ auth.css styles .btn-signal for a <button>, which has no underline to
       reset -- so the moment the portal used the same class on an <a>, every
       primary action grew a link underline through the middle of it. Measured:
       text-decoration-line: underline on a.btn-signal. The portal is where
       these became anchors, so the reset lives here rather than in auth.css.
   Also re-centres: a <button> centres its own label, an <a> does not. */
a.btn-signal { text-decoration: none; text-align: center; }
a.btn-signal:hover { text-decoration: none; }

/* =======================  PAGE HEADER WITH AN ACTION  ====================
   The page title and the screen's primary action on one row.
   /!\ The action is NOT promoted above the heading when this wraps -- see the
       responsive block at the foot of this file. A full-width bar above the
       title reads as a toolbar and pushes the page name off a phone screen. */
.pt-head-row {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1.2rem 1.5rem; flex-wrap: wrap;
}
.pt-head-text { min-width: 0; flex: 1 1 18rem; }
.pt-head-actions {
    display: flex; align-items: center; gap: 0.6rem;
    flex: 0 0 auto; padding-top: 0.2rem;
}
.pt-head-actions .btn-signal { width: auto; min-width: 0; padding-inline: 1.15rem; }

/* =======================  THE REVIEW STEP  ==============================
   Hidden until the script shows it, and hidden by a CLASS rather than the
   `hidden` attribute: `.pt-review` sets its own display, which would win over
   the user-agent rule for [hidden] and leave the panel permanently visible. */
.pt-review { display: none; }
.pt-review.is-on { display: block; }
.pt-review:focus { outline: none; }
.pt-is-hidden { display: none; }

.pt-review-kv {
    display: grid; grid-template-columns: minmax(8rem, 13rem) minmax(0, 1fr);
    gap: 0 1.2rem; margin: 0;
}
.pt-review-kv dt {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.11em; text-transform: uppercase;
    color: var(--faint); padding: 0.7rem 0; border-top: 1px solid var(--line);
}
.pt-review-kv dd {
    margin: 0; padding: 0.7rem 0; border-top: 1px solid var(--line);
    color: var(--text); font-size: 0.9rem; line-height: 1.6;
    /* The narrative can be long and is not the reporter's problem to wrap. */
    overflow-wrap: anywhere;
}
.pt-review-kv dt:first-of-type,
.pt-review-kv dd:first-of-type { border-top: 0; }
/* The reporter's own prose keeps the line breaks they typed -- collapsing them
   shows them something different from what they wrote. */
.pt-review-kv dd.is-pre { white-space: pre-wrap; }
.pt-review-kv dd.is-blank { color: var(--faint); font-style: italic; }

.pt-compose-actions { align-items: center; }

/* =======================  UNSENT DRAFTS  ================================
   Quiet on purpose, and visibly not a report. A draft has not been filed, has
   no reference and nobody in Internal Audit can see it -- so it must not borrow
   the surface the real reports sit on, or it reads as something that has been
   raised. Dashed edge, no card fill, muted type: the visual language of a note
   to yourself rather than a record. */
.pt-drafts {
    border: 1px dashed var(--line-2);
    border-radius: 12px;
    padding: 1rem 1.1rem;
    margin-bottom: 1.5rem;
    background: transparent;
}
.pt-drafts-head { margin-bottom: 0.7rem; }
.pt-drafts-title {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.64rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--muted); margin: 0 0 0.25rem;
    display: flex; align-items: center; gap: 0.5rem;
}
/* A count, not a warning -- so it is NOT the signal colour. Amber on this
   screen means "this needs you", and an unsent draft does not. */
.pt-drafts-n {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.25rem; height: 1.25rem; padding: 0 0.35rem;
    border: 1px solid var(--line-2); border-radius: 999px;
    font-size: 0.62rem; letter-spacing: 0; color: var(--text);
}
.pt-drafts-sub { margin: 0; font-size: 0.8rem; color: var(--faint); }

.pt-drafts-list { list-style: none; margin: 0; padding: 0; }
.pt-draft {
    display: flex; align-items: center; justify-content: space-between;
    gap: 0.8rem 1rem; flex-wrap: wrap;
    padding: 0.7rem 0; border-top: 1px solid var(--line);
}
.pt-draft:first-child { border-top: 0; }
.pt-draft-main {
    display: flex; flex-direction: column; gap: 0.15rem;
    min-width: 0; flex: 1 1 14rem; text-decoration: none;
}
.pt-draft-title {
    color: var(--text); font-size: 0.9rem; line-height: 1.4;
    overflow-wrap: anywhere;
}
.pt-draft-main:hover .pt-draft-title { color: var(--signal-2); }
.pt-draft-when {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.08em; color: var(--faint);
}
.pt-draft-acts { display: flex; align-items: center; gap: 0.6rem; flex: 0 0 auto; }
.pt-draft-del { margin: 0; }
.pt-btn-mini { padding: 0.36rem 0.75rem; font-size: 0.75rem; }

/* =======================  THE ACCOUNT BLOCK  ============================
   Who you are, at the top of the sidebar, and the way to your profile.
   Deliberately not a nav item: it is an identity, not a destination, and it
   should not compete with the three places people actually go. */
.pt-acct {
    display: flex; align-items: center; gap: 0.6rem;
    padding: 0.7rem 0.75rem; margin: 0 0 0.9rem;
    border: 1px solid var(--line); border-radius: 10px;
    text-decoration: none; color: inherit;
    transition: border-color 160ms ease, background 160ms ease;
}
a.pt-acct:hover { border-color: var(--line-2); background: var(--hover); }
a.pt-acct:hover .pt-acct-go { color: var(--signal); transform: translateX(2px); }
/* Not a link when the profile is unreachable: no hover, no chevron, no lie. */
.pt-acct.is-static { cursor: default; }

.pt-acct-chip {
    flex: 0 0 auto;
    width: 30px; height: 30px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--signal-soft); color: var(--signal-2);
    border: 1px solid rgb(var(--signal-rgb) / 0.3);
    font-family: "IBM Plex Mono", monospace; font-size: 0.8rem; line-height: 1;
}
.pt-acct-txt { min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.pt-acct-name {
    color: var(--text); font-size: 0.82rem; line-height: 1.25;
    /* One line. A long name must not push the address out of the block. */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pt-acct-mail {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.6rem; letter-spacing: 0.02em; color: var(--faint);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pt-acct-go {
    margin-left: auto; flex: 0 0 auto; color: var(--faint);
    display: inline-flex; transition: color 160ms ease, transform 160ms ease;
}

/* Visually hidden, still read aloud. The chevron alone does not say where the
   block goes, and the name is not a description of a link. */
.pt-sr {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* =======================  THE MODE SWITCHER  ===========================
   Three segments. Quiet: choosing how to read a page is not an action anybody
   needs pointed out, so no amber and no fill until one is current. */
.pt-modes { margin: 0 0 0.8rem; }
.pt-modes-lbl {
    display: block;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.56rem; letter-spacing: 0.2em; text-transform: uppercase;
    color: var(--faint); margin-bottom: 0.35rem;
}
.pt-modes-set {
    display: flex; border: 1px solid var(--line); border-radius: 8px;
    overflow: hidden;
}
.pt-mode {
    flex: 1 1 0; min-width: 0;
    appearance: none; background: transparent; cursor: pointer;
    border: 0; border-left: 1px solid var(--line);
    padding: 0.4rem 0.2rem;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--muted);
    transition: background 150ms ease, color 150ms ease;
}
.pt-mode:first-child { border-left: 0; }
.pt-mode:hover { background: var(--hover); color: var(--text); }
/* The current one is filled, not coloured: amber on this portal means "this
   needs your attention", and a display preference does not. */
.pt-mode.is-on { background: var(--ink-3); color: var(--text); }

/* On the entry screen it sits at the foot of the stage, under the metadata
   line, at the width of its own content rather than the column's. */
.pt-stage .pt-modes { margin: 1.1rem 0 0; max-width: 15rem; }

/* ==============  auth.css VALUES THAT ONLY WORK ON INK  =================
   auth.css hardcodes a handful of colours that are correct for one dark
   palette and wrong for the two light ones. They are overridden HERE, scoped
   to .pt, because auth.css is shared with /login and re-theming it there would
   change the staff sign-in screen.

   /!\ MEASURED, NOT GUESSED. The primary button came out at 3.76:1 in
       `daylight` -- auth.css sets `color: #1a1300` on .btn-signal, which is
       near-black on bright amber (fine) and near-black on the DARKER amber a
       light ground needs (muddy and hard to read). --on-signal is the fix and
       every mode defines it. */
.pt .btn-signal { color: var(--on-signal); }

/* /!\ EVERY FIELD WAS THE SAME WHITE AS THE CARD BEHIND IT, and on the light
       modes that made the form unreadable as a form -- "i didnt like ... make
       user confused what need to be inserted and what to be read" (the user,
       2026-09-20). Measured: the card came back rgb(255 255 255) and so did
       .control, because auth.css fills it with --ink-3 and --ink-3 IS white on
       both light palettes. On the dark one --ink-3 is lighter than the card, so
       the same rule works there and the fault was invisible.

   /!\ SO A FIELD NOW HAS ITS OWN TONE, and the direction flips with the
       ground: raised out of the card on ink, recessed into it on paper. What
       matters is only that a control is never the same tone as its surface.

   /!\ AND FOCUS BRIGHTENS ON PAPER RATHER THAN DARKENING. auth.css sets
       `background: #171b22` on .control:focus-within -- a hardcoded near-black
       that is right on ink and turns a light field into a black box the moment
       somebody clicks into it. */
.pt .control {
    background: var(--field);
    border-color: var(--line-2);
}
.pt .control:focus-within {
    background: var(--field-focus);
    border-color: var(--signal);
}
/* /!\ THERE WAS A SECOND `.pt .control:focus-within` FURTHER DOWN THIS FILE,
       added when the light modes were built to undo the same hardcoded
       near-black. Two rules, one selector, identical specificity -- so the
       LATER one silently won and this one was dead the moment it was written.
       Found by measuring the computed background, not by reading. There is one
       rule now; if a third appears, the same thing happens again. */

/* /!\ THE NATIVE DROPDOWN POPUP IS DRAWN BY THE OS, and auth.css asks for a
       dark one (`color-scheme: dark` on .control select) because it was written
       for one dark palette. On paper that is a black list dropping out of a
       white field -- the same mismatch, one layer down, and the comment in
       auth.css says as much about the opposite case. */
.pt[data-pt-theme="daylight"] .control select,
.pt[data-pt-theme="slate"] .control select { color-scheme: light; }

/* The chevron auth.css draws into the select is a fixed grey that reads on ink
   and washes out on paper; it is the only part of the control the border and
   the fill cannot carry on their own. */
.pt[data-pt-theme="daylight"] .control select,
.pt[data-pt-theme="slate"] .control select {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%2357637a' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* A label names a field; a hint explains it. They were the same weight, which
   is the other half of "what do I type and what do I read". */
.pt .field > label { color: var(--muted); }
.pt .field-hint { color: var(--faint); }

/* The shimmer that sweeps the button on hover is white -- invisible against a
   pale ground, and on the light modes the button is the darkest thing on the
   screen, so it sweeps the other way. */
.pt .btn-signal::before {
    background: linear-gradient(90deg, transparent, rgb(var(--signal-rgb) / 0.35), transparent);
}
.pt[data-pt-theme="daylight"] .btn-signal::before,
.pt[data-pt-theme="slate"] .btn-signal::before {
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.28), transparent);
}

/* The stage's two atmospheric washes are an AMBER glow and a BLUE glow at fixed
   alphas, tuned for near-black. On near-white the amber one is a visible cream
   tint over the whole panel -- which made `slate`, the COOL mode, read warm.
   Measured: the stage sat on #eaeef4 and looked cream in a screenshot.
   Derived from the mode's own signal and halved on paper. */
.pt .stage {
    background:
        radial-gradient(120% 90% at 12% 0%, rgb(var(--signal-rgb) / 0.08), transparent 55%),
        radial-gradient(80% 70% at 100% 100%, rgba(40, 70, 120, 0.12), transparent 60%),
        var(--ink-2);
}
.pt[data-pt-theme="daylight"] .stage,
.pt[data-pt-theme="slate"] .stage {
    background:
        radial-gradient(120% 90% at 12% 0%, rgb(var(--signal-rgb) / 0.05), transparent 55%),
        radial-gradient(80% 70% at 100% 100%, rgba(40, 70, 120, 0.05), transparent 60%),
        var(--ink-2);
}

/* The flash strip carries the same three states the portal's own notes do, so
   it uses the same tokens rather than its own dark literals. */
.pt .auth-flash.is-success { color: var(--note-ok-fg);   background: var(--note-ok-bg); }
.pt .auth-flash.is-error   { color: var(--note-err-fg);  background: var(--note-err-bg);
                             border-left-color: var(--note-err-line); }
.pt .auth-flash.is-info    { color: var(--note-info-fg); background: var(--note-info-bg); }
.pt .auth-flash-close:hover { background: var(--hover); }

/* The case screen's top row: back on the left, the document on the right. */
.pt-case-top {
    display: flex; align-items: center; justify-content: space-between;
    gap: 0.8rem 1rem; flex-wrap: wrap; margin-bottom: 1.1rem;
}
.pt-case-top .pt-back { margin: 0; }
.pt-dl { display: inline-flex; margin-right: 0.35rem; vertical-align: -2px; }

/* =======================  THE SIGNAL BOARD  =============================
   The signed-in dashboard, rebuilt 2026-09-18. Data-forward and calm: mono for
   labels, references and counts, Hanken for prose, and AMBER ONLY where
   something needs the reader. If amber appears on a screen with no open
   questions, that is a bug. */

/* /!\ THE PAGE HEADER IS NO LONGER STICKY, AND THAT IS NOT A REGRESSION. It
       was sticky so the action inside it stayed reachable; the action is in the
       bar now, which is sticky for every screen and every panel at once and
       cannot be pushed down the page by adding a block above it. A second
       sticky element under the first would just be two bars. */

/* ---- 1. NEEDS YOU ---- */
.pt-needs {
    border: 1px solid rgb(var(--signal-rgb) / 0.35);
    background: rgb(var(--signal-rgb) / 0.06);
    border-radius: 12px; padding: 1rem 1.1rem; margin-bottom: 1.5rem;
}
.pt-needs-head { margin-bottom: 0.8rem; }
.pt-needs-title {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--signal); margin: 0 0 0.3rem; font-weight: 600;
    display: flex; align-items: center; gap: 0.5rem;
}
.pt-needs-n {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.3rem; height: 1.3rem; padding: 0 0.35rem; border-radius: 999px;
    background: var(--signal); color: var(--on-signal);
    font-size: 0.66rem; letter-spacing: 0;
}
.pt-needs-sub { margin: 0; font-size: 0.85rem; color: var(--text); }

.pt-q-list { list-style: none; margin: 0; padding: 0; }
.pt-q {
    display: grid;
    grid-template-columns: 9.5rem minmax(0, 1fr) auto auto;
    align-items: baseline; gap: 0.3rem 0.9rem;
    padding: 0.65rem 0; border-top: 1px solid rgb(var(--signal-rgb) / 0.2);
}
.pt-q:first-child { border-top: 0; }
.pt-q-ref {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.7rem; letter-spacing: 0.04em; color: var(--muted);
}
.pt-q-text { color: var(--text); font-size: 0.88rem; line-height: 1.5; min-width: 0; }
.pt-q-age {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.66rem; color: var(--muted); white-space: nowrap;
}
.pt-q-go {
    text-decoration: none; white-space: nowrap;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--on-signal); background: var(--signal);
    padding: 0.32rem 0.7rem; border-radius: 6px;
}
.pt-q-go:hover { background: var(--signal-2); }

/* The opposite state. One quiet line, and no amber anywhere on the screen. */
/* /!\ `.pt-calm` IS GONE (2026-09-19). "Nothing is waiting on you." was a
       full-width band between two rules announcing that nothing had happened --
       one of the largest single pieces of the empty space the user objected to.
       The NEEDS YOU cell reading 0 says the same thing where the reader is
       already looking, and costs no height at all. */

/* ---- 2. WHERE EVERYTHING STANDS ----
   The house KPI tile (MD-006 13.2): a 36px icon chip on the left, the number
   over an uppercase label. The same shape Leave Summary (.ls-kpi) and the
   Department Dashboard (.dk) draw, at the same proportions -- 36px chip, 19px
   glyph, 23px number, 10px label.

   /!\ THE PATTERN IS COPIED, NOT THE STYLESHEET. theme.css is the application
       bundle and this page deliberately does not load it, so there is no
       --color-surface-solid and no --color-muted here. Reproducing the geometry
       in the portal's own tokens is the only way these can match without the
       portal growing a dependency on the app shell it exists to stay out of.

   /!\ THE FIVE STATES ALWAYS SUM TO THE TOTAL. Do not drop a tile to shorten
       the row -- a report counted in the total and shown in no state is a row
       that does not reconcile.

   /!\ AMBER ON `NEEDS YOU` ALONE, and only when it is non-zero. The house tile
       colours its chip per tile via --kc; here every chip is neutral except
       that one, because on this screen amber means "this needs you". */
.pt-kpis {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr));
    gap: 10px; margin-bottom: 1.1rem;
}
.pt-kpi {
    position: relative; overflow: hidden;
    display: flex; align-items: center; gap: 12px; text-align: left;
    padding: 12px 16px; border-radius: 12px; min-width: 0;
    border: 1px solid var(--line); background: var(--ink-2);
}
/* /!\ THE ACCENT IS A LEFT EDGE AND A CHIP, NEVER THE WHOLE CARD. A tinted
       card behind a number makes the number harder to read and turns six tiles
       into six competing blocks; the edge is enough to tell them apart at a
       glance, which is all the colour is for. */
.pt-kpi::before {
    content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
    background: rgb(var(--kc) / 0.75);
}
.pt-kpi-ico {
    flex: none; width: 36px; height: 36px; border-radius: 10px;
    display: flex; align-items: center; justify-content: center;
    color: rgb(var(--kc)); background: rgb(var(--kc) / 0.13);
}
.pt-kpi-body { display: flex; flex-direction: column; min-width: 0; line-height: 1.1; }
.pt-kpi-n {
    font-family: "IBM Plex Mono", monospace;
    font-size: 23px; font-weight: 600; color: var(--text);
    font-variant-numeric: tabular-nums; letter-spacing: -0.02em;
}
.pt-kpi-l {
    margin-top: 3px;
    font-family: "IBM Plex Mono", monospace;
    font-size: 10px; font-weight: 500; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--faint);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The Total is a sum, not a state, so it is fenced off rather than merely
   first -- otherwise it reads as the beginning of the sequence. */
.pt-kpi.is-total { background: var(--ink-3); border-color: var(--line-2); }

/* A zero is greyed, never hidden: a missing tile and an empty one look
   identical and only one of them means anything. The CHIP goes quiet with it,
   so a row of zeroes does not read as six things happening. */
.pt-kpi.is-zero .pt-kpi-n { color: var(--faint); }
.pt-kpi.is-zero::before { background: var(--line-2); }
.pt-kpi.is-zero .pt-kpi-ico { color: var(--faint); background: var(--ink-3); }

/* /!\ NEEDS-YOU IS THE ONLY TILE WHOSE ACCENT LEAVES THE CHIP. The other five
       colour an edge and a glyph; this one tints the card and takes the number
       and the label too, because amber on this screen means "do something" and
       nothing else may borrow it. */
.pt-kpi.is-signal {
    border-color: rgb(var(--signal-rgb) / 0.4);
    background: rgb(var(--signal-rgb) / 0.07);
}
.pt-kpi.is-signal .pt-kpi-n,
.pt-kpi.is-signal .pt-kpi-l { color: var(--signal); }

/* ---- 3. THE SUPPORTING PANELS, UNDERNEATH ----
   /!\ THEY WERE A RAIL BESIDE THE LIST FOR ONE DAY. The user's own structure
       puts them BELOW the reports instead, so the case list owns the full
       width: the page is a register with context under it, not a dashboard
       with a list attached. Do not promote them back up the page. */
.pt-support {
    display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
    gap: 1rem; align-items: start; margin-top: 1.4rem;
}
.pt-panel {
    border: 1px solid var(--line); border-radius: 10px;
    padding: 0.85rem 0.95rem; background: var(--ink-2); min-width: 0;
}
.pt-panel-h {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--faint); margin: 0 0 0.85rem; font-weight: 400;
}
.pt-panel-empty { margin: 0; font-size: 0.85rem; color: var(--faint); }

/* /!\ THE ACTIVITY FEED SCROLLS IN ITSELF, like the register (the user's
       call, 2026-09-20: "Recent activity and report record table must each one
       has scroll up down so the screen will not go too much down"). Ten entries
       is already a screenful; the feed is context and must not push the page
       down on the reader's behalf.
   /!\ Dropped on a phone with everything else -- a scroll region inside a
       scrolling page traps the gesture. */
.pt-panel-scroll {
    max-height: 300px;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-right: 2px;
}

.pt-acts { list-style: none; margin: 0; padding: 0; }
/* Timestamp above the event, reference right and quiet -- the brief's own
   hierarchy. The stamp is the thing an activity feed is read by. */
.pt-act {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.1rem 0.8rem; align-items: baseline;
    padding: 0.42rem 0; border-top: 1px solid var(--line);
}
.pt-act:first-child { border-top: 0; padding-top: 0; }
.pt-act-when {
    grid-column: 1 / -1;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.6rem; letter-spacing: 0.06em;
    color: var(--faint); white-space: nowrap;
}
.pt-act-what { font-size: 0.82rem; color: var(--text); min-width: 0; }
.pt-act-ref {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.64rem; color: var(--muted); text-decoration: none; white-space: nowrap;
}
.pt-act-ref:hover { color: var(--signal); }

.pt-raised-lead { margin: -0.25rem 0 0.7rem; font-size: 0.84rem; color: var(--text); }
/* A two-column summary rather than dot leaders: the brief asked for
   "Category | Reports", which is a table, and a table is what a manager copies
   into something else.
   /!\ STILL NO CHART. At these counts a row reads faster, and a pie here would
       make this a generic analytics page rather than a reporting product. */
.pt-brk { width: 100%; border-collapse: collapse; margin-top: 0.55rem; }
.pt-brk th {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.55rem; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--faint); font-weight: 400; text-align: left;
    padding: 0 0 0.35rem; border-bottom: 1px solid var(--line);
}
.pt-brk th:last-child, .pt-brk td:last-child { text-align: right; width: 4.5rem; }
.pt-brk td {
    padding: 0.32rem 0; font-size: 0.82rem; color: var(--text);
    border-bottom: 1px solid var(--line);
}
.pt-brk tr:last-child td { border-bottom: 0; }
.pt-brk td:last-child {
    font-family: "IBM Plex Mono", monospace; font-size: 0.78rem; color: var(--muted);
    font-variant-numeric: tabular-nums;
}

/* =======================  THE REGISTER  =================================
   Its own screen until 2026-09-19; the body of the home page now. It keeps a
   heading of its own because it sits beside a rail of other headings, and an
   unlabelled list next to two labelled panels reads as the page's leftovers. */
.pt-reg-wrap { min-width: 0; }

/* /!\ THIS BAR IS STICKY, AND THAT IS THE OTHER HALF OF MOVING THE ACTION.
       "+ Report a Concern" belongs to this section rather than to the global
       bar (the user's call) -- but a section header scrolls away, and the very
       first complaint this portal ever got was that with many records the
       create button ends up below the fold. Pinning the header keeps it
       reachable at any scroll position without putting a global control back.

   /!\ OPAQUE, and it must stay so: the rows scroll underneath it. Every
       surface token in this portal is solid, which is what makes that safe. */
/* /!\ NOT STICKY ANY MORE, AND THAT IS A SIMPLIFICATION. It was pinned so the
       action would survive a long list; the ROWS scroll in their own region now
       (.pt-reg-scroll), so this header never moves and nothing scrolls past it.
       If the scroll region is ever removed, the sticky has to come back or the
       create button goes below the fold again -- the first complaint this
       portal ever got. */
.pt-reg-bar {
    padding: 0.2rem 0 0.6rem;
    border-bottom: 1px solid var(--line-2);
    margin-bottom: 0.2rem;
}

/* /!\ THE ROWS SCROLL, THE HEADER DOES NOT, AND THE PANELS BELOW STAY
       REACHABLE. Without this a long register pushed Recent activity and
       Reporting activity off the bottom of the page entirely (the user's
       observation, 2026-09-19).
   /!\ THE MAX-HEIGHT IS DROPPED ON A PHONE -- see the responsive block. A
       scroll region inside a scrolling page traps the gesture. */
.pt-reg-scroll {
    max-height: calc(100vh - 360px);
    min-height: 120px;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Room for the scrollbar so the rows do not shift when one appears. */
    padding-right: 2px;
}
.pt-reg-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
}
.pt-reg-titles { min-width: 0; }
.pt-sec-h {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--text); margin: 0 0 0.15rem; font-weight: 500;
}
.pt-reg-count { margin: 0; font-size: 0.76rem; color: var(--faint); }

.pt-reg-new.btn-signal {
    width: auto; min-width: 0; height: auto;
    padding: 0.5rem 0.95rem;
    display: inline-flex; align-items: center; gap: 0.45rem;
    font-size: 0.72rem; flex: 0 0 auto;
}
.pt-reg-new-plus { font-size: 1rem; line-height: 1; font-weight: 400; }

/* /!\ `pt-fchip`, NOT `pt-chip`, AND THE RENAME IS A BUG FIX. `.pt-chip` was
       already taken, 900 lines above this, by the entry screen's
       "Confidential - not anonymous" status chip -- so from the day the filter
       shipped, every rule here was also landing on that chip and winning on
       source order. It changed its colour, its border, its padding and its
       type, on the first screen a reporter sees, with nothing in either block
       to say the other existed. The same class of fault as `.ring` on the Leave
       Summary gauges: a name that looks free and is not. */
.pt-fchips { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.6rem 0 0; }
.pt-fchip {
    appearance: none; cursor: pointer;
    background: transparent; border: 1px solid var(--line);
    border-radius: 999px; padding: 0.34rem 0.75rem;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.64rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--muted);
    display: inline-flex; align-items: center; gap: 0.4rem;
    transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
}
.pt-fchip:hover { color: var(--text); border-color: var(--line-2); }
/* Filled, not amber: choosing what to look at is not something that needs you. */
.pt-fchip.is-on { background: var(--ink-3); color: var(--text); border-color: var(--line-2); }
.pt-fchip-n { color: var(--faint); font-size: 0.6rem; }

.pt-year {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.18em; color: var(--faint);
    margin: 0.9rem 0 0.2rem; font-weight: 400;
}
.pt-reg { list-style: none; margin: 0; padding: 0; }
.pt-reg-row { border-top: 1px solid var(--line); }
.pt-reg-row:first-child { border-top: 0; }
.pt-reg-row.is-waiting { background: rgb(var(--signal-rgb) / 0.05); }
/* The brief's row: reference, subject, then one meta line. Scannable top to
   bottom in three beats rather than across two columns. */
.pt-reg-link {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    gap: 0.12rem 0.8rem; padding: 0.6rem 0.55rem;
    text-decoration: none; align-items: center;
}
.pt-reg-link:hover { background: var(--hover); }
.pt-reg-ref {
    grid-column: 1;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.08em; color: var(--faint);
}
.pt-reg-title {
    grid-column: 1;
    color: var(--text); font-size: 0.9rem; font-weight: 550; line-height: 1.35;
    overflow-wrap: anywhere;
}
.pt-reg-link:hover .pt-reg-title { color: var(--signal-2); }
.pt-reg-meta {
    grid-column: 1; display: flex; flex-wrap: wrap; align-items: center;
    gap: 0.3rem; font-size: 0.72rem; color: var(--faint); margin-top: 0.15rem;
}
/* A chevron, so the row reads as something that opens rather than as a block
   of text that happens to be clickable. */
.pt-reg-go {
    grid-column: 2; grid-row: 1 / -1;
    display: flex; color: var(--faint); opacity: 0.6;
    transition: color var(--motion-duration), opacity var(--motion-duration);
}
.pt-reg-link:hover .pt-reg-go { color: var(--signal); opacity: 1; }
.pt-reg-none { margin: 0.9rem 0 0; font-size: 0.84rem; color: var(--faint); }

/* The status badge is compact and NEUTRAL in a register. Amber is reserved for
   .pt-pill-signal, which says "Needs you" and means it. */
.pt-reg-meta .pt-pill { padding: 0.22rem 0.5rem; font-size: 0.56rem; letter-spacing: 0.12em; }


/* ---- account requests, for a portal administrator ----
   /!\ THIS SCREEN CARRIES NO CASE DATA. It reuses the register's row shell
       because it is the same kind of list -- a short queue of things waiting on
       a decision -- not because it shows anything about a report. */
.pt-req { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.pt-req-who {
    display: flex; flex-direction: column; gap: 0.1rem;
    padding: 0.7rem 0.55rem; min-width: 0;
}
.pt-req-mail {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.7rem; color: var(--muted); overflow-wrap: anywhere;
}
.pt-req-acts {
    display: flex; align-items: center; gap: 0.8rem;
    flex: 0 0 auto; margin: 0; padding-right: 0.4rem;
}
.pt-req-ok.btn-signal {
    width: auto; min-width: 0; height: auto;
    padding: 0.45rem 0.9rem; font-size: 0.7rem;
}
.pt-req-note {
    margin: 1rem 0 0; font-size: 0.78rem; line-height: 1.5; color: var(--faint);
    max-width: 46rem;
}

/* The count beside a menu entry. Neutral, not amber: it is a number, and what
   needs the reader is already said by the dot on the circle. */
.pt-menu-n {
    margin-left: auto; flex: 0 0 auto;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; color: var(--muted);
    background: var(--ink-3); border-radius: 999px; padding: 0.1rem 0.4rem;
}

/* ---- evidence a reporter can open again ----
   The filename became a link on 2026-09-19. The glyph is a document rather than
   a download arrow on purpose: these open, they are not being handed over. */
.pt-file-link {
    display: inline-flex; align-items: center; gap: 0.4rem;
    color: var(--text); text-decoration: none;
    border-bottom: 1px solid var(--line-2);
    transition: color var(--motion-duration), border-color var(--motion-duration);
}
.pt-file-link:hover { color: var(--signal); border-color: var(--signal); }
.pt-file-link:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.pt-file-go { display: flex; flex: 0 0 auto; color: var(--faint); }
.pt-file-link:hover .pt-file-go { color: var(--signal); }

/* =======================  PANELS  =======================================
   A panel is an existing route rendered with no page around it and laid over
   the one you are on: the composer, a report, the profile. Asked for on
   2026-09-19 -- "new creation must be popup box fill all page with professional
   design ... and also the edit same popup box".

   /!\ IT IS A SHEET, NOT A DIALOG BOX. The content is a whole screen -- a
       twelve-field composer, or a report with its correspondence and its
       evidence list -- and a centred box with its own scrollbar in the middle
       of a page that also scrolls is two scroll contexts fighting. It fills the
       viewport, insets slightly on a large screen so the page underneath still
       reads as underneath, and goes edge to edge on a phone.

   /!\ EVERY PANEL IS A REAL ROUTE. With JavaScript off the same links navigate
       to the same pages and nothing here is loaded at all.
   ======================================================================== */

.pt-overlay { position: fixed; inset: 0; z-index: 60; }
.pt-overlay[hidden] { display: none; }

.pt-overlay-scrim {
    position: absolute; inset: 0;
    background: rgb(0 0 0 / 0.55);
    animation: pt-fade 160ms ease both;
}
/* Ink is already near-black, so a dark scrim alone barely separates the two
   layers. The light modes get the opposite problem, so each states its own. */
.pt[data-pt-theme="daylight"] .pt-overlay-scrim,
.pt[data-pt-theme="slate"]    .pt-overlay-scrim { background: rgb(18 20 24 / 0.4); }

/* /!\ IT IS SIZED TO ITS CONTENT, NOT TO THE SCREEN, AND THAT IS THE THIRD
       TIME THIS HAS MOVED. It began inset 2.2rem with a 760px column stranded
       inside it (two grey margins beside a twelve-field form); it went full
       bleed on 2026-09-19; and full bleed stopped reading as a popup at all --
       "it must show that is popup box able to be close and open ... make it
       take size of records" (2026-09-20). So the WIDTH is the content's measure
       plus its padding, the HEIGHT is whatever the content needs up to 88vh,
       and the page stays visible around it. The measure still lives on
       .pt-panel-doc; the dialog is drawn around it rather than the other way
       round.

   /!\ THE SCRIM IS WHAT MAKES IT A DIALOG. Do not remove it to "clean up" the
       look: without something dimming the page behind, a floating card over a
       live screen reads as part of that screen. */
.pt-sheet {
    position: absolute;
    top: 50%; left: 50%; transform: translate(-50%, -50%);
    display: flex; flex-direction: column;
    /* width: the content measure + the body's own padding, either side. */
    width: min(calc(760px + 5rem), calc(100vw - 3rem));
    max-height: 88vh;
    background: var(--ink);
    border: 1px solid var(--line-2); border-radius: 16px;
    box-shadow: 0 30px 80px rgb(0 0 0 / 0.45);
    overflow: hidden;
    animation: pt-rise 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* The composer is not prose and asks for the wider measure; the dialog grows
   with it rather than the content shrinking to fit the dialog. */
.pt-sheet.is-roomy { width: min(calc(1040px + 5rem), calc(100vw - 3rem)); }

.pt-sheet-body {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    padding: 2.2rem 2.5rem 2.6rem;
}
/* The fragment brings its own inner wrapper; this is what gives the panel the
   same measure as the page underneath, so a form does not change width when it
   moves from one to the other.
   /!\ 760px IS A MEASURE FOR PROSE -- a record's thread, a profile. A twelve
       field composer is not prose, and asks for `is-roomy` (see form.php),
       which is the same distinction .pt-roomy draws on the page. */
.pt-panel-doc { max-width: 760px; margin: 0 auto; }
.pt-panel-doc.is-roomy { max-width: 1040px; }

.pt-sheet-x {
    position: absolute; top: 0.7rem; right: 0.75rem; z-index: 2;
    width: 34px; height: 34px; border-radius: 999px;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--ink-3); color: var(--muted);
    border: 1px solid var(--line); cursor: pointer;
    transition: color var(--motion-duration), border-color var(--motion-duration);
}
.pt-sheet-x:hover { color: var(--signal); border-color: var(--signal); }
.pt-sheet-x:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* The frame a panel opens on, before its markup arrives. */
.pt-frame { padding-top: 0.5rem; }
.pt-frame-ref {
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.68rem; letter-spacing: 0.1em; color: var(--signal);
    margin: 0 0 0.5rem;
}
.pt-frame-txt { margin: 0; color: var(--faint); font-size: 0.9rem; }

/* While a panel is fetching. Not a spinner: the sheet is either the old
   content or the new one, and a flash of nothing in between is worse than a
   moment of dimming. */
.pt-overlay.is-busy .pt-sheet-body { opacity: 0.45; transition: opacity 120ms ease; }

/* The page underneath must not scroll behind the sheet. */
body.pt-locked { overflow: hidden; }

@keyframes pt-fade { from { opacity: 0; } to { opacity: 1; } }
/* /!\ IT SCALES FROM THE CENTRE NOW, because the dialog is centred with a
       transform -- a keyframe that ends at `transform: none` would snap it to
       the top-left corner on the last frame. The translate(-50%, -50%) has to
       be carried in every frame. */
@keyframes pt-rise {
    from { opacity: 0; transform: translate(-50%, -50%) scale(0.97); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
/* The portal already honours this elsewhere; a sheet that slides is the most
   obvious place somebody would notice it not being honoured. */
@media (prefers-reduced-motion: reduce) {
    .pt-sheet, .pt-overlay-scrim { animation: none; }
}

/* =======================  THE IDLE WARNING  =============================
   Appears only in the last two minutes, and only for a signed-in account.

   /!\ IT IS NOT A MODAL, DELIBERATELY. Somebody may be part-way through
       writing a report; a dialogue that takes the keyboard away to tell them
       about the session is the fault this replaced, made worse. It is a bar in
       the corner that can be ignored, and ignoring it does exactly what it
       says.
   ======================================================================== */
.pt-idle {
    position: fixed; z-index: 70; bottom: 1.1rem; right: 1.1rem;
    display: flex; align-items: center; gap: 0.9rem;
    max-width: calc(100vw - 2.2rem);
    padding: 0.7rem 0.9rem 0.7rem 1rem;
    background: var(--ink-2);
    border: 1px solid rgb(var(--signal-rgb) / 0.4);
    border-radius: 11px;
    box-shadow: 0 12px 34px rgb(0 0 0 / 0.35);
    animation: pt-rise 200ms ease both;
}
.pt-idle-txt { font-size: 0.82rem; color: var(--text); }
.pt-idle-txt strong {
    font-family: "IBM Plex Mono", monospace;
    font-variant-numeric: tabular-nums; color: var(--signal); font-weight: 600;
}
.pt-idle-stay {
    flex: 0 0 auto; cursor: pointer;
    font-family: "IBM Plex Mono", monospace;
    font-size: 0.62rem; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--signal); background: none;
    border: 1px solid var(--signal); border-radius: 8px;
    padding: 0.45rem 0.7rem;
}
.pt-idle-stay:hover { background: rgb(var(--signal-rgb) / 0.12); }
a.pt-idle-stay { text-decoration: none; display: inline-block; }
/* Once it has actually run out there is nothing to count, so it stops looking
   like a countdown. */
.pt-idle.is-over { border-color: var(--line-2); }

/* =======================  RESPONSIVE  ====================================
   /!\ Everything below this line overrides rules above it and must stay at the
       bottom of the file — see the header note. */

@media (max-width: 900px) {
    /* Stacked, the stage is the top of the page, not a column beside it. */
    .pt-wide .stage { position: static; height: auto; }
    .pt-stage-full { min-height: 0; }
    .pt-stage-full .headline { font-size: clamp(2rem, 7vw, 2.6rem); }
    /* Wider than the desktop column, but still a measure: `max-width: none`
       here ran the promises to ~95 characters a line at 900px of stacked stage. */
    .pt-stage-full .stage-head { max-width: 38rem; }
    .pt-promise { margin-top: 1.4rem; }
    .pt-stage-full .stage-foot { display: none; }
}

@media (max-width: 760px) {
    /* A four-column question row is unreadable narrow: reference over question,
       with the age and Reply sharing the line below. */
    .pt-q { grid-template-columns: minmax(0, 1fr) auto; gap: 0.25rem 0.7rem; }
    .pt-q-ref  { grid-column: 1 / -1; }
    .pt-q-text { grid-column: 1 / -1; }
}

@media (max-width: 620px) {
    .pt-draft-acts { width: 100%; }
    .pt-draft-acts .pt-btn-mini { flex: 1 1 auto; text-align: center; }
    /* Stacked, the action sits UNDER the title and subtitle -- still above the
       content it acts on, never above the page name. */
    .pt-head-actions { width: 100%; padding-top: 0.2rem; }
    .pt-head-actions .btn-signal,
    .pt-head-actions .pt-btn-quiet { flex: 1 1 auto; text-align: center; }
    .pt-review-kv { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .pt-review-kv dd { border-top: 0; padding-top: 0; }
    .pt-review-kv dt { padding-bottom: 0.25rem; }
}

@media (max-width: 520px) {
    .pt-tab { font-size: 0.55rem; letter-spacing: 0.1em; }
    .pt-ref { font-size: 1.1rem; letter-spacing: 0.07em; }
    .control input.pt-code { font-size: 1.15rem; letter-spacing: 0.34em; text-indent: 0.34em; }
    .pt-kv td:first-child { width: 42%; }
    .pt-actions .btn-signal { min-width: 0; width: 100%; }
    .pt-btn-quiet { width: 100%; }
}

/* The tiles re-flow on their own (auto-fit), so only the panels need a rule. */
@media (max-width: 1000px) {
    .pt-support { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 860px) {
    .pt-main { padding: 1.6rem 1.1rem 2.4rem; }
    .pt-bar { padding-inline: 1rem; }
    .pt-sheet-body { padding: 2rem 1.2rem 2.6rem; }
    /* In a menu this narrow the label would push the three segments onto a
       second line for a word nobody needs; the buttons say what they do. */
    .pt-menu .pt-modes { padding-inline: 0.45rem; }
    .pt-modes-lbl { display: none; }
    .pt-mode { padding: 0.4rem 0.55rem; }
}

@media (max-width: 620px) {
    /* An approve button and a reject link beside a name is three things on one
       phone line; the actions take their own row under the person. */
    .pt-req { flex-direction: column; align-items: stretch; gap: 0.2rem; }
    .pt-req-acts { padding: 0 0.55rem 0.7rem; }
    .pt-bar-brand-txt em { display: none; }
    .pt-menu { width: min(17rem, calc(100vw - 1.6rem)); }
    /* /!\ THE ACTION COLLAPSES TO A "+" AND MUST NOT WRAP TO A SECOND ROW. A
           two-row sticky header eats a chunk of a phone screen on every scroll,
           which is the complaint this header exists to answer. */
    .pt-reg-new .btn-label { display: none; }
    .pt-reg-new.btn-signal { padding: 0.5rem 0.7rem; }
    .pt-reg-new-plus { font-size: 1.15rem; }
}

/* /!\ NO NESTED SCROLL ON A PHONE. A scroll region inside a scrolling page
       traps the gesture: a drag that starts on a row scrolls the list and never
       the page, so the panels under it become unreachable by the very control
       meant to keep them reachable. The list flows with the page instead. */
@media (max-width: 760px) {
    .pt-reg-scroll,
    .pt-panel-scroll { max-height: none; overflow-y: visible; padding-right: 0; }
    /* A dialog on a phone IS the screen -- there is no room to show the page
       around it, and a 3rem inset on 375px is most of the width. */
    .pt-sheet, .pt-sheet.is-roomy {
        width: 100%; max-height: 100%;
        top: 0; left: 0; transform: none;
        border: 0; border-radius: 0;
        /* /!\ THE KEYFRAME CARRIES translate(-50%, -50%) for the centred
               dialog, so it must NOT run here -- it would animate the sheet
               half off the screen on every open. Fade instead. */
        animation-name: pt-fade;
    }
    .pt-sheet-body { padding: 2rem 1.2rem 2.6rem; }
}

@media (max-width: 560px) {
    /* The chip goes before the number does -- the house rule. */
    .pt-kpi { padding: 10px 12px; gap: 9px; }
    .pt-kpi-ico { display: none; }
    .pt-kpi-n { font-size: 20px; }
}

@media (max-width: 460px) {
    .pt-empty { padding: 1.9rem 1.1rem; }
    .pt-empty .btn-signal { width: 100%; min-width: 0; }
    .pt-idle { left: 1.1rem; right: 1.1rem; }
}
