/* ============================================================================
   theme.css — the house design system for every app on this site.
   Phase 7 of the portal plan (2026-11-05).

   HOW TO ADOPT IT, SAFELY
   Add this one line to a page, above the page's own <style>:

       <link rel="stylesheet" href="/shared/theme.css">

   Nothing changes visually on a page that already styles itself: this file
   loads FIRST, so the page's own rules win every collision. It only supplies
   what the page has not already said. That is deliberate — nine apps adopt it
   one at a time, and adding the line is never the risky step. The risky step
   is deleting the app's own CSS afterwards, which happens per app, in daylight.

   EVERYTHING IS PREFIXED `ph-` — tokens and components both.

   The first draft left tokens unprefixed (--navy, --bg, --ink…) so that an
   app's own values would flow into the shared components. Measuring the nine
   pages killed that idea: they already define the same names with different
   values. --amber is #9a6b00, #b7791f, #d9a327 AND #e8a33d depending on the
   app; --navy has three variants, --ink four, --muted three. A shared
   component reading those would render in whatever that page happened to mean
   by "amber", which is not a design system, just a coincidence.

   So `--ph-*` is the source of truth and no app defines those names. A
   component looks the same in every app, and an app's own tokens are left
   completely alone. Class names are prefixed for the same reason: Trade union,
   Budget and Asset management each have a `.card` that means something
   different, and none of them is touched by `.ph-card`.

   ONE DELIBERATE CORRECTION to the incumbent palette, below at --ph-muted.

   THE HOUSE FACE IS INTER (decided by the owner 2026-11-05, replacing Be Vietnam Pro
   earlier the same day). Inter covers Vietnamese, including stacked tone marks.
   Every page that loads this file is set in Inter — see "The house face, everywhere"
   below — except code and monospaced figures, which keep --ph-mono.

   THE TYPE SCALE (owner, 2026-11-05, second version — replaces the larger 30/22/15
   scale from earlier the same day). All Inter. The owner's own variable names:
     main page title    Bold 700      18px   --font-page-title    (22 → 18: owner, 20% smaller)
     section heading    SemiBold 600  14px   --font-section-title (17 → 14: owner, 20% smaller)
     body text          Regular 400   13px   --font-body
     navigation menu    Medium 500    12px   --font-navigation
     tables             Regular 400   12px   --font-table
     KPI numbers        SemiBold 600  22px   --font-kpi-value
     KPI labels         Medium 500    11px   --font-kpi-label
     buttons / filters  Medium 500    12px   --font-button
     chart labels       Regular 400   11px   --font-chart
     secondary text     Regular 400   11px   --font-secondary
   The older --ph-fs-* names point at these, so anything already using them follows.
   Classes: .ph-type-title / -section / -body / -nav / -table.

   The @import below keeps adoption to one line. A page may also put the same
   Google Fonts <link> in its own head — same URL, fetched once either way.
   If Google Fonts is unreachable the stack falls back to Segoe UI and nothing breaks.
============================================================================ */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap');

:root{
  /* ---- Brand ------------------------------------------------------------ */
  --ph-navy:#0B1F3A;          /* headers, headings, primary buttons            */
  --ph-navy2:#2b3f5e;         /* focused borders, hover on navy                */
  --ph-navy3:#15304f;         /* pressed/hover fill on navy buttons            */
  --ph-navy-line:#3a4f70;     /* borders ON navy                               */
  --ph-amber:#e8a33d;         /* the one accent — markers, active, attention   */
  --ph-amber-soft:#fdf3e3;    /* amber at page strength                        */

  /* ---- Surfaces --------------------------------------------------------- */
  --ph-bg:#f1f3f7;
  --ph-card:#fff;
  --ph-bd:#dfe3ea;
  --ph-bd-strong:#c6cdd9;     /* table rules, dividers that must read          */
  --ph-ink:#1c2530;

  /* --muted was #6e7580, which is 4.23:1 on --bg — under AA for body text.
     #656c77 is the same hue at 4.78:1 on --bg and 5.30:1 on --card. It is the
     only colour in this file that is not the incumbent value, and it is a
     correction, not a redesign. */
  --ph-muted:#656c77;
  --ph-muted-on-navy:#9fb8dc; /* 8.1:1 on --navy                               */
  --ph-faint-on-navy:#cfdcef;

  /* ---- Status ----------------------------------------------------------- */
  --ph-ok-bg:#e8f5ec;  --ph-ok-ink:#1e7a44;  --ph-ok-bd:#bfe3cb;
  --ph-warn-bg:#fdf3e3; --ph-warn-ink:#8a5a12; --ph-warn-bd:#f0dcb4;
  --ph-err-bg:#fdecec; --ph-err-ink:#9b2c2c; --ph-err-bd:#f5c6c6;
  --ph-chip-bg:#eef1f6; --ph-chip-ink:#50607a;

  /* ---- Type ------------------------------------------------------------- */
  --ph-sans:"Inter","Segoe UI",system-ui,-apple-system,sans-serif;
  /* The owner's type scale (2026-11-05), see the header. */
  --font-family:'Inter', sans-serif;
  --font-page-title:18px;
  --font-section-title:14px;
  --font-body:13px;
  --font-navigation:12px;
  --font-table:12px;
  --font-kpi-value:22px;
  --font-kpi-label:11px;
  --font-button:12px;
  --font-chart:11px;
  --font-secondary:11px;
  --ph-fs-title:var(--font-page-title);       --ph-fw-title:700;
  --ph-fs-section:var(--font-section-title);  --ph-fw-section:600;
  --ph-fs-body:var(--font-body);              --ph-fw-body:400;
  --ph-fs-nav:var(--font-navigation);         --ph-fw-nav:500;
  --ph-fs-table:var(--font-table);            --ph-fw-table:400;
  --ph-mono:"JetBrains Mono","Cascadia Mono","Consolas",ui-monospace,monospace;
  --ph-fs-ui:13px;            /* the house size: chrome, tables, controls      */
  --ph-fs-prose:14px;         /* paragraphs and descriptions only              */
  /* Sentences that explain something are not labels. 11.5px is fine for an
     uppercase column head or a chip; it is too small for a line someone has
     to read. Anything written as a sentence uses --fs-note or larger. */
  --ph-fs-note:12.5px;
  --ph-fs-small:11.5px;       /* labels, chips, uppercase heads — never prose  */
  --ph-fs-h:12px;             /* section heading, uppercase                    */
  --ph-lh:1.5;

  /* ---- Space, shape, depth ---------------------------------------------- */
  --ph-s1:4px; --ph-s2:8px; --ph-s3:12px; --ph-s4:16px; --ph-s5:22px; --ph-s6:32px; --ph-s7:48px;
  --ph-r-ctl:6px;             /* buttons, inputs                               */
  --ph-r-card:8px;            /* cards, panels                                 */
  --ph-r-pill:999px;
  --ph-shadow-1:0 1px 3px rgba(11,31,58,.07), 0 1px 1px rgba(11,31,58,.04);
  --ph-shadow-2:0 2px 10px rgba(11,31,58,.10), 0 1px 3px rgba(11,31,58,.06);
  --ph-shadow-pop:0 10px 30px rgba(11,31,58,.16), 0 2px 8px rgba(11,31,58,.08);

  /* ---- Motion ----------------------------------------------------------- */
  --ph-ease:cubic-bezier(.16,1,.3,1);   /* exponential ease-out                */
  --ph-t-fast:120ms;
  --ph-t:200ms;
}

/* ============================================================================
   Browser surfaces. The parts nobody draws still belong to the design.
============================================================================ */
::selection{background:var(--ph-amber);color:#2a1b02}
:root{accent-color:var(--ph-navy); caret-color:var(--ph-navy2); color-scheme:light}

/* The house face, everywhere (owner, 2026-11-05: "fix all the font to Inter").
   `html body` (0,0,2) outranks an app's own `body{font-family}` (0,0,1), and the
   `*` rule makes controls and every other element inherit it — buttons and inputs
   do not inherit fonts on their own. Anything an app set with a CLASS or an ID —
   its monospace figures, a printed form's own face — still wins, by specificity. */
html body{font-family:var(--ph-sans)}
html body *{font-family:inherit}
html body :is(code,kbd,pre,samp,tt){font-family:var(--ph-mono)}

/* The type scale as classes (sizes in the header). */
.ph-type-title{font-size:var(--ph-fs-title);font-weight:var(--ph-fw-title);line-height:1.2;letter-spacing:-.015em}
.ph-type-section{font-size:var(--ph-fs-section);font-weight:var(--ph-fw-section);line-height:1.3;letter-spacing:-.01em}
.ph-type-body{font-size:var(--ph-fs-body);font-weight:var(--ph-fw-body);line-height:1.6}
.ph-type-nav{font-size:var(--ph-fs-nav);font-weight:var(--ph-fw-nav)}
.ph-type-table{font-size:var(--ph-fs-table);font-weight:var(--ph-fw-table)}

/* Anything marked .ph is in the house system, whatever the host page's own
   body font says. A converted app puts .ph on <body>; an app still being
   converted puts it only on the part that has moved over. */
.ph{font-family:var(--ph-sans)}

/* Without this, .ph-input's width:100% means 100% PLUS its padding and border,
   and the field hangs 22px past its container. Scoped to .ph so adopting the
   stylesheet never changes the box model of a host page that has not moved
   over yet. (Found by measuring a field that looked flush and was not.) */
.ph,.ph *,.ph *::before,.ph *::after{box-sizing:border-box}

.ph *{scrollbar-width:thin;scrollbar-color:var(--ph-bd-strong) transparent}
.ph ::-webkit-scrollbar{width:11px;height:11px}
.ph ::-webkit-scrollbar-track{background:transparent}
.ph ::-webkit-scrollbar-thumb{background:var(--ph-bd-strong);border-radius:var(--ph-r-pill);
  border:3px solid transparent;background-clip:content-box}
.ph ::-webkit-scrollbar-thumb:hover{background:var(--ph-muted);background-clip:content-box}

/* One focus ring for the whole suite, visible on both light and navy. */
.ph :focus-visible{outline:2px solid var(--ph-amber);outline-offset:2px;border-radius:3px}

/* Figures line up in columns everywhere, without being asked per table. */
.ph table td, .ph table th, .ph .ph-num, .ph input[type=number]{
  font-variant-numeric:tabular-nums}

/* ============================================================================
   Shell
============================================================================ */
.ph-head{background:var(--ph-navy);color:#fff;height:52px;padding:0 20px;
  display:flex;align-items:center;gap:var(--ph-s4)}
.ph-head a{color:inherit}
.ph-head .ph-brand{font-weight:700;text-transform:uppercase;letter-spacing:.08em;
  font-size:var(--ph-fs-ui);color:#fff;text-decoration:none;white-space:nowrap}
.ph-head .ph-sub{color:var(--ph-muted-on-navy);font-size:var(--ph-fs-small)}
.ph-head .ph-spacer{flex:1}
.ph-main{max-width:1280px;margin:0 auto;padding:var(--ph-s5) 20px var(--ph-s7)}
.ph-main.ph-narrow{max-width:1060px}

/* The house section rule: small uppercase navy over a 2px navy underline.
   More space above than below, so a heading belongs to what follows it. */
.ph-h{font-size:var(--ph-fs-h);text-transform:uppercase;letter-spacing:.1em;
  color:var(--ph-navy);margin:var(--ph-s6) 0 var(--ph-s3);padding-bottom:6px;
  border-bottom:2px solid var(--ph-navy)}
.ph-h:first-child{margin-top:var(--ph-s1)}
.ph-lead{color:var(--ph-muted);font-size:var(--ph-fs-prose);margin:0 0 var(--ph-s4)}

/* ============================================================================
   Buttons. Every state is drawn: hover, active, focus, disabled, busy.
============================================================================ */
.ph-btn{font:inherit;cursor:pointer;display:inline-flex;align-items:center;gap:6px;
  padding:7px 14px;border-radius:var(--ph-r-ctl);border:1px solid var(--ph-bd-strong);
  background:var(--ph-card);color:var(--ph-ink);white-space:nowrap;
  transition:background var(--ph-t-fast) var(--ph-ease),border-color var(--ph-t-fast) var(--ph-ease),
             box-shadow var(--ph-t-fast) var(--ph-ease),transform var(--ph-t-fast) var(--ph-ease)}
.ph-btn:hover{background:#f7f9fc;border-color:var(--ph-muted)}
.ph-btn:active{transform:translateY(1px)}
.ph-btn[disabled],.ph-btn[aria-disabled=true]{opacity:.5;cursor:not-allowed;
  transform:none;background:var(--ph-card);border-color:var(--ph-bd)}

.ph-btn-primary{background:var(--ph-navy);border-color:var(--ph-navy);color:#fff}
.ph-btn-primary:hover{background:var(--ph-navy3);border-color:var(--ph-navy3)}
.ph-btn-danger{background:var(--ph-err-ink);border-color:var(--ph-err-ink);color:#fff}
.ph-btn-danger:hover{background:#852525;border-color:#852525}
.ph-btn-ghost{background:transparent;border-color:transparent;color:var(--ph-muted)}
.ph-btn-ghost:hover{background:var(--ph-chip-bg);border-color:transparent;color:var(--ph-ink)}
.ph-head .ph-btn-ghost{color:var(--ph-faint-on-navy);border-color:var(--ph-navy-line)}
.ph-head .ph-btn-ghost:hover{background:#1b3254;color:#fff}
.ph-btn-sm{padding:4px 10px;font-size:var(--ph-fs-small)}

/* Busy: the label stays (so the button keeps its width) and a ring spins. */
.ph-btn[data-busy=true]{color:transparent;pointer-events:none;position:relative}
.ph-btn[data-busy=true]::after{content:"";position:absolute;inset:0;margin:auto;
  width:14px;height:14px;border-radius:50%;border:2px solid currentColor;
  border-top-color:transparent;color:var(--ph-ink);animation:ph-spin .7s linear infinite}
.ph-btn-primary[data-busy=true]::after,.ph-btn-danger[data-busy=true]::after{color:#fff}
@keyframes ph-spin{to{transform:rotate(360deg)}}

/* ============================================================================
   Fields
============================================================================ */
.ph-label{display:block;font-size:var(--ph-fs-small);color:var(--ph-muted);margin:var(--ph-s3) 0 3px;
  font-weight:600;letter-spacing:.02em}
.ph-input,.ph-select,.ph-textarea{width:100%;font:inherit;color:var(--ph-ink);
  padding:7px 10px;border:1px solid var(--ph-bd);border-radius:var(--ph-r-ctl);background:var(--ph-card);
  transition:border-color var(--ph-t-fast) var(--ph-ease),box-shadow var(--ph-t-fast) var(--ph-ease)}
.ph-input:hover,.ph-select:hover{border-color:var(--ph-bd-strong)}
.ph-input:focus,.ph-select:focus,.ph-textarea:focus{outline:none;border-color:var(--ph-navy2);
  box-shadow:0 0 0 3px rgba(43,63,94,.13)}
.ph-input::placeholder{color:var(--ph-muted);opacity:1}
.ph-input[disabled],.ph-select[disabled]{background:var(--ph-chip-bg);color:var(--ph-muted);cursor:not-allowed}
.ph-input[aria-invalid=true]{border-color:var(--ph-err-ink)}
.ph-hint{font-size:var(--ph-fs-note);color:var(--ph-muted);margin-top:3px}
.ph-field-err{font-size:var(--ph-fs-note);color:var(--ph-err-ink);margin-top:3px;font-weight:600}

/* ============================================================================
   Surfaces
============================================================================ */
.ph-card{background:var(--ph-card);border:1px solid var(--ph-bd);border-radius:var(--ph-r-card);
  padding:var(--ph-s3) var(--ph-s4)}
.ph-panel{background:var(--ph-card);border:1px solid var(--ph-bd);border-radius:var(--ph-r-card);
  padding:var(--ph-s5);box-shadow:var(--ph-shadow-1)}
.ph-toolbar{display:flex;gap:var(--ph-s3);align-items:center;flex-wrap:wrap;margin-bottom:var(--ph-s3)}
.ph-toolbar .ph-spacer{flex:1}
/* A flex child's min-width is auto, so a full-width input inside one pushes the
   row wider than its container and the whole page gains a sideways scrollbar.
   It costs one line here and saves finding it nine times. */
.ph-toolbar>*{min-width:0}

/* Nested cards are always wrong: inside a card, a group is a ruled band. */
.ph-band{border-top:1px solid var(--ph-bd);padding:var(--ph-s3) 0;margin:0}
.ph-band:first-child{border-top:0;padding-top:0}
.ph-band:last-child{padding-bottom:0}

/* ============================================================================
   Chips and status
============================================================================ */
.ph-chip{display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:600;
  padding:2px 9px;border-radius:var(--ph-r-pill);background:var(--ph-chip-bg);color:var(--ph-chip-ink);
  letter-spacing:.02em;white-space:nowrap}
.ph-chip-ok{background:var(--ph-ok-bg);color:var(--ph-ok-ink)}
.ph-chip-warn{background:var(--ph-warn-bg);color:var(--ph-warn-ink)}
.ph-chip-err{background:var(--ph-err-bg);color:var(--ph-err-ink)}
.ph-chip-dot::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor}

.ph-msg{border-radius:var(--ph-r-ctl);padding:9px 12px;margin:0 0 var(--ph-s3);
  font-size:var(--ph-fs-ui);border:1px solid transparent}
.ph-msg-ok{background:var(--ph-ok-bg);color:var(--ph-ok-ink);border-color:var(--ph-ok-bd)}
.ph-msg-warn{background:var(--ph-warn-bg);color:var(--ph-warn-ink);border-color:var(--ph-warn-bd)}
.ph-msg-err{background:var(--ph-err-bg);color:var(--ph-err-ink);border-color:var(--ph-err-bd)}

/* ============================================================================
   Tables — the shape most of these apps actually are
============================================================================ */
.ph-table{width:100%;font-size:var(--ph-fs-table);border-collapse:separate;border-spacing:0;background:var(--ph-card);
  border:1px solid var(--ph-bd);border-radius:var(--ph-r-card);overflow:hidden}
.ph-table th{background:#f6f8fb;color:var(--ph-navy);font-size:var(--ph-fs-small);
  text-transform:uppercase;letter-spacing:.06em;font-weight:700;text-align:left;
  padding:8px 12px;border-bottom:1px solid var(--ph-bd-strong);position:sticky;top:0;z-index:1}
.ph-table td{padding:8px 12px;border-bottom:1px solid var(--ph-bd);vertical-align:top}
.ph-table tbody tr:last-child td{border-bottom:0}
.ph-table tbody tr{transition:background var(--ph-t-fast) var(--ph-ease)}
.ph-table tbody tr:hover{background:#f8fafd}
.ph-table .ph-right{text-align:right}
.ph-table-fixed{table-layout:fixed}

/* ============================================================================
   Empty, loading and error states — written, not left to chance
============================================================================ */
.ph-empty{text-align:center;padding:var(--ph-s7) var(--ph-s4);color:var(--ph-muted)}
.ph-empty .ph-empty-t{color:var(--ph-ink);font-weight:600;font-size:var(--ph-fs-prose);
  margin-bottom:var(--ph-s1)}
.ph-empty .ph-empty-d{max-width:46ch;margin:0 auto var(--ph-s4);font-size:var(--ph-fs-prose)}
.ph-skel{background:linear-gradient(90deg,var(--ph-chip-bg) 25%,#e4e9f1 37%,var(--ph-chip-bg) 63%);
  background-size:400% 100%;border-radius:4px;animation:ph-shimmer 1.4s ease infinite}
@keyframes ph-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}

/* ============================================================================
   The permission matrix — one component, used in every app's Settings and in
   the portal-wide admin page. Capability rows x role columns.
============================================================================ */
.ph-matrix{width:100%;border-collapse:separate;border-spacing:0;background:var(--ph-card);
  border:1px solid var(--ph-bd);border-radius:var(--ph-r-card);overflow:hidden}
.ph-matrix th{background:#f6f8fb;font-size:var(--ph-fs-small);color:var(--ph-navy);
  text-transform:uppercase;letter-spacing:.06em;font-weight:700;padding:9px 10px;
  border-bottom:1px solid var(--ph-bd-strong);text-align:center;white-space:nowrap}
.ph-matrix th:first-child{text-align:left;width:42%}
.ph-matrix td{padding:7px 10px;border-bottom:1px solid var(--ph-bd);text-align:center}
.ph-matrix td:first-child{text-align:left}
.ph-matrix tbody tr:last-child td{border-bottom:0}
.ph-matrix tbody tr:hover{background:#f8fafd}
.ph-matrix .ph-cap{font-weight:600;color:var(--ph-ink)}
.ph-matrix .ph-cap-d{color:var(--ph-muted);font-size:var(--ph-fs-note);margin-top:1px}
.ph-matrix .ph-grp td{background:var(--ph-chip-bg);font-size:var(--ph-fs-small);font-weight:700;
  color:var(--ph-chip-ink);text-transform:uppercase;letter-spacing:.06em;text-align:left}

/* A checkbox that reads as a decision rather than a default form control. */
.ph-tick{appearance:none;width:18px;height:18px;border:1px solid var(--ph-bd-strong);
  border-radius:4px;background:var(--ph-card);cursor:pointer;position:relative;vertical-align:middle;
  transition:background var(--ph-t-fast) var(--ph-ease),border-color var(--ph-t-fast) var(--ph-ease)}
.ph-tick:hover{border-color:var(--ph-navy2)}
.ph-tick:checked{background:var(--ph-navy);border-color:var(--ph-navy)}
.ph-tick:checked::after{content:"";position:absolute;left:5px;top:1px;width:5px;height:10px;
  border:solid #fff;border-width:0 2px 2px 0;transform:rotate(42deg)}
.ph-tick[disabled]{background:var(--ph-chip-bg);border-color:var(--ph-bd);cursor:not-allowed}
.ph-tick[disabled]:checked{background:var(--ph-muted);border-color:var(--ph-muted)}
.ph-tick-dirty{box-shadow:0 0 0 3px rgba(232,163,61,.35)}

/* ============================================================================
   Settings layout — the same two-column shape in every app
============================================================================ */
.ph-settings{display:grid;grid-template-columns:200px 1fr;gap:var(--ph-s5);align-items:start}
.ph-settings-nav{position:sticky;top:var(--ph-s3);display:flex;flex-direction:column;gap:2px}
.ph-settings-nav button{font:inherit;text-align:left;cursor:pointer;background:transparent;
  border:0;border-left:2px solid transparent;color:var(--ph-muted);padding:7px 12px;
  border-radius:0 var(--ph-r-ctl) var(--ph-r-ctl) 0;
  transition:background var(--ph-t-fast) var(--ph-ease),color var(--ph-t-fast) var(--ph-ease)}
.ph-settings-nav button:hover{background:var(--ph-chip-bg);color:var(--ph-ink)}
.ph-settings-nav button[aria-current=true]{background:var(--ph-chip-bg);color:var(--ph-navy);
  font-weight:700;border-left-color:var(--ph-amber)}
@media (max-width:760px){
  .ph-settings{grid-template-columns:1fr}
  .ph-settings-nav{position:static;flex-direction:row;overflow-x:auto;
    border-bottom:1px solid var(--ph-bd);padding-bottom:var(--ph-s2)}
  .ph-settings-nav button{border-left:0;border-bottom:2px solid transparent;border-radius:var(--ph-r-ctl)}
  .ph-settings-nav button[aria-current=true]{border-bottom-color:var(--ph-amber)}
}

/* ============================================================================
   The one authored moment: a view settles into place when its data arrives.
   Used once per view, on the container — never on every row and every card.
============================================================================ */
.ph-enter{animation:ph-enter 420ms var(--ph-ease) both}
@keyframes ph-enter{
  from{opacity:0;transform:translateY(6px);filter:blur(3px)}
  to{opacity:1;transform:none;filter:none}
}

@media (prefers-reduced-motion:reduce){
  .ph *,.ph *::before,.ph *::after{
    animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important;scroll-behavior:auto!important}
}

/* ============================================================================
   Utilities
============================================================================ */
.ph-row{display:flex;align-items:center;gap:var(--ph-s2)}
.ph-col{display:flex;flex-direction:column;gap:var(--ph-s2)}
.ph-spacer{flex:1}
.ph-muted{color:var(--ph-muted)}
.ph-right{text-align:right}
.ph-mono{font-family:var(--ph-mono);font-size:12px}
.ph-hide{display:none!important}
.ph-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

@media print{
  .ph-head,.ph-settings-nav,.ph-toolbar,.ph-btn{display:none!important}
  .ph-main{max-width:none;padding:0}
  .ph-table,.ph-matrix{border:0}
}

/* ============================================================================
   THE APP SHELL (owner, 2026-11-05) — one navy bar on top of every app, the same
   bar as the landing page, so moving between apps does not jump.
   Built by shared/app-shell.js; styled here so every page gets the same thing.

     [▦ all apps] PLAZA HOTEL  App name ............ [page actions] [EN|VI] (●)

   · The bar never changes colour or height from app to app (--ps-bar-h).
   · Buttons on it are white outlines with a faint fill — present, never louder
     than the account button.
   · The way back to the landing page: the grid button, the brand, and
     "All apps" in the account menu.
   · Sidebars (navigation) sit BELOW the bar: one look in every app — the app's
     name on top, then groups (small capitals, fold with ▾), then items, with
     sub-items under a thin guide line. Each app maps its own markup to it.
============================================================================ */
:root{--ps-bar-h:48px;--ps-head-h:52px;--ps-bar-bg:#0B1F3A;--ps-side-bg:#2b3f5e;--ps-side-hover:#243755;
  --ps-side-ink:#dbe2ee;--ps-side-muted:#b4c1d6;--ps-side-line:#3a4f70}
.ps-bar{position:sticky;top:0;z-index:900;height:var(--ps-bar-h);flex:none;
  background:var(--ps-bar-bg);color:#fff;display:flex;align-items:center;gap:12px;
  padding:0 14px 0 10px;font-family:var(--ph-sans);font-size:var(--font-navigation);line-height:1;
  box-sizing:border-box;width:100%}
.ps-bar *{box-sizing:border-box}
.ps-home{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;
  border-radius:8px;color:#cfdcef;flex:none}
.ps-home:hover{background:rgba(255,255,255,.08);color:#fff}
.ps-home svg{width:16px;height:16px}
.ps-brand{font-weight:700;text-transform:uppercase;letter-spacing:.08em;font-size:13px;
  color:#fff;text-decoration:none;white-space:nowrap}
.ps-brand:hover{color:#fff;text-decoration:underline;text-underline-offset:4px;text-decoration-color:var(--ph-amber)}
.ps-spacer{flex:1 1 auto;min-width:8px}
.ps-actions{display:flex;align-items:center;gap:8px;min-width:0;overflow-x:auto;scrollbar-width:none}
.ps-actions::-webkit-scrollbar{display:none}
.ps-actions:empty{display:none}
/* Every control placed on the bar, whatever the app called it. `html body` keeps
   these above the apps' own button rules; app rules written with an #id still win,
   so apps drop those when they move a button here. */
html body .ps-bar :is(.ps-actions button,.ps-actions a,.ps-actions .btn,.ps-actions label,.ps-btn){
  height:30px;display:inline-flex;align-items:center;gap:6px;padding:0 11px;margin:0;
  border-radius:6px;border:1px solid rgba(255,255,255,.24);background:rgba(255,255,255,.04);
  color:#fff;font:500 var(--font-button)/1 var(--ph-sans);letter-spacing:0;text-transform:none;
  white-space:nowrap;text-decoration:none;cursor:pointer;box-shadow:none;
  transition:background var(--ph-t-fast) var(--ph-ease),border-color var(--ph-t-fast) var(--ph-ease)}
html body .ps-bar :is(.ps-actions button,.ps-actions a,.ps-actions .btn,.ps-actions label,.ps-btn):hover{
  background:rgba(255,255,255,.11);border-color:rgba(255,255,255,.42);color:#fff}
html body .ps-bar :is(.ps-actions button,.ps-actions a,.ps-actions .btn,.ps-btn):focus-visible{
  outline:2px solid var(--ph-amber);outline-offset:2px}
html body .ps-bar :is(.ps-actions button,.ps-btn):disabled{opacity:.45;cursor:not-allowed}
html body .ps-bar .ps-actions svg{stroke:currentColor;color:#fff}
html body .ps-bar .ps-actions .ic{margin-right:0}
/* Status chip (Live / Demo / Offline): not a button, so no border. */
html body .ps-bar .ps-actions .ps-status{border-color:transparent;background:rgba(255,255,255,.08);cursor:default;font-weight:600}
/* ---- Bar buttons, one format in every app (owner, 2026-11-05: Management's bar is the model):
   white outline, faint fill, a drawn icon then the label. PortalShell picks the icon from the
   label (Export → download, Print → printer, Reload → arrows…) and sets data-ps-ic; a button
   that already carries its own <svg> keeps it. Icons are masks, so they take the text colour. */
[data-ps-ic=cloud]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.5%2012.5h7a3%203%200%200%200%20.4-6A4%204%200%200%200%204.2%207.6%202.5%202.5%200%200%200%204.5%2012.5z'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=lock]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Crect%20x%3D'3.5'%20y%3D'7'%20width%3D'9'%20height%3D'6.5'%20rx%3D'1'%2F%3E%3Cpath%20d%3D'M5.5%207V5a2.5%202.5%200%200%201%205%200v2'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=unlock]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Crect%20x%3D'3.5'%20y%3D'7'%20width%3D'9'%20height%3D'6.5'%20rx%3D'1'%2F%3E%3Cpath%20d%3D'M5.5%207V5a2.5%202.5%200%200%201%204.9-.7'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=download]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M8%202.5v8M4.8%207.5%208%2010.7l3.2-3.2M3%2013.5h10'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=upload]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M8%2010.5v-8M4.8%205.7%208%202.5l3.2%203.2M3%2013.5h10'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=print]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.5%206V2.5h7V6M4.5%2011.5H3a.5.5%200%200%201-.5-.5V6.5A.5.5%200%200%201%203%206h10a.5.5%200%200%201%20.5.5V11a.5.5%200%200%201-.5.5h-1.5'%2F%3E%3Crect%20x%3D'4.5'%20y%3D'9.5'%20width%3D'7'%20height%3D'4'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=refresh]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M13%208a5%205%200%201%201-1.5-3.6M13%202.5v2.8h-2.8'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=columns]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Crect%20x%3D'2.5'%20y%3D'3'%20width%3D'11'%20height%3D'10'%20rx%3D'1'%2F%3E%3Cpath%20d%3D'M6.2%203v10M9.8%203v10'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=reset]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M3%208a5%205%200%201%200%201.5-3.6M3%202.5v2.8h2.8'%2F%3E%3C%2Fsvg%3E")}
[data-ps-ic=save]{--ps-ic:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M3%202.5h8l2%202v9H3z'%2F%3E%3Cpath%20d%3D'M5.5%202.5v3h5v-3M5%2013.5v-4h6v4'%2F%3E%3C%2Fsvg%3E")}
:root{--ps-ic-cloud:url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2016%2016'%20fill%3D'none'%20stroke%3D'black'%20stroke-width%3D'1.5'%20stroke-linecap%3D'round'%20stroke-linejoin%3D'round'%3E%3Cpath%20d%3D'M4.5%2012.5h7a3%203%200%200%200%20.4-6A4%204%200%200%200%204.2%207.6%202.5%202.5%200%200%200%204.5%2012.5z'%2F%3E%3C%2Fsvg%3E")}
html body .ps-bar .ps-actions [data-ps-ic]::before{content:'';width:14px;height:14px;flex:none;background:currentColor;
  -webkit-mask:var(--ps-ic) center/contain no-repeat;mask:var(--ps-ic) center/contain no-repeat}
/* Status chips (Live / Offline / Could not load): drawn exactly like Management's "Live" button,
   the cloud coloured by the state — green live, amber working, red failed. Not clickable. */
html body .ps-bar .ps-actions :is(.live,.ps-status){height:30px;display:inline-flex;align-items:center;gap:6px;padding:0 11px;
  border-radius:6px;border:1px solid rgba(255,255,255,.24);background:rgba(255,255,255,.04);color:#fff;
  font:500 var(--font-button)/1 var(--ph-sans);white-space:nowrap;flex:none;cursor:default}
html body .ps-bar .ps-actions :is(.live,.ps-status)::before{content:'';width:14px;height:14px;flex:none;background:#ff8a7a;
  -webkit-mask:var(--ps-ic-cloud) center/contain no-repeat;mask:var(--ps-ic-cloud) center/contain no-repeat}
html body .ps-bar .ps-actions :is(.live.ok,.ps-status:not(.err):not(.warn))::before{background:#5fd08f}
html body .ps-bar .ps-actions .ps-status.warn::before{background:var(--ph-amber)}
html body .ps-bar .ps-actions :is(.live i,.ps-dot){display:none}
/* EN | VI */
.ps-lang{display:inline-flex;height:30px;border:1px solid rgba(255,255,255,.24);border-radius:6px;overflow:hidden;flex:none}
.ps-lang button{border:0;background:transparent;color:#b9c8de;padding:0 9px;font:500 var(--font-button)/1 var(--ph-sans);cursor:pointer}
.ps-lang button:hover{color:#fff;background:rgba(255,255,255,.08)}
.ps-lang button[aria-pressed=true]{background:rgba(255,255,255,.16);color:#fff;font-weight:600}
.ps-lang button:focus-visible{outline:2px solid var(--ph-amber);outline-offset:-2px}
.ps-acct{display:inline-flex;flex:none}
.ps-acct:empty{display:none}
/* The page title under the bar (the owner's scale: main title 30–36 bold). */
.ps-title{font-size:var(--font-page-title);font-weight:700;line-height:1.2;letter-spacing:-.015em;color:var(--ph-navy);margin:0}
/* The sidebar's title: just the app's name. */
/* The sidebar's title: just the app's name, the same size as the page title (18 bold) and
   in a row exactly as tall as the page header beside it (--ps-head-h), so the two titles
   and the two rules under them line up (owner, 2026-11-05). Each app gives its page
   header min-height:var(--ps-head-h). */
.ps-side-title{display:flex;align-items:center;box-sizing:border-box;height:var(--ps-head-h);margin:0;padding:0 18px;
  font-size:var(--font-page-title);font-weight:700;line-height:1.2;letter-spacing:-.01em;color:#fff;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-bottom:1px solid var(--ps-side-line);flex:none}
@media (max-width:760px){
  .ps-bar{gap:8px;padding:0 10px 0 6px}
}
@media print{.ps-bar{display:none!important}}

/* ---- The sidebar, the same in every app (owner, 2026-11-05) -------------------
   Asset management's folder tree is the model:
     GROUP ▾ ............ n      small capitals; click folds the group
     │ Item                      13.5px medium; the open screen is lit, amber marker
     │   └ Sub-item              13px, one step in, joined by a thin line
   Apps whose menu is flat (a group label followed by its items) get the folding
   from PortalShell.foldNav(); apps that draw their own tree (Asset) use the
   same values in their own rules. */
.ps-nav-grp{display:flex;align-items:center;gap:10px;width:100%;margin:0;border:0;border-radius:0;background:none;
  padding:13px 16px 5px;color:var(--ps-side-muted);font:600 var(--font-secondary)/1.2 var(--ph-sans);letter-spacing:.09em;
  text-transform:uppercase;text-align:left;cursor:pointer}
.ps-nav-grp:hover{color:#fff;background:none}
.ps-nav-grp:focus-visible{outline:2px solid var(--ph-amber);outline-offset:-2px}
.ps-nav-grp .ps-car{width:10px;height:10px;flex:none;transition:transform var(--ph-t-fast) var(--ph-ease)}
.ps-nav-grp[aria-expanded=false] .ps-car{transform:rotate(-90deg)}
.ps-nav-grp .ps-n{margin-left:auto;font-size:var(--font-secondary);font-weight:500;letter-spacing:0;opacity:.75}
.ps-kids{margin-left:22px;border-left:1px solid var(--ps-side-line)}
.ps-kids[hidden]{display:none}
html body .ps-nav-item{display:flex;align-items:center;gap:8px;width:100%;margin:0;text-align:left;
  padding:7px 16px 7px 16px;border:0;border-left:3px solid transparent;border-radius:0;background:none;
  color:var(--ps-side-ink);font:500 var(--font-navigation)/1.35 var(--ph-sans);text-decoration:none;cursor:pointer}
html body .ps-nav-item:hover{background:var(--ps-side-hover);color:#fff}
html body .ps-nav-item:is(.on,.active,[aria-current=page]){background:var(--ps-side-hover);color:#fff;font-weight:600;border-left-color:var(--ph-amber)}
html body .ps-nav-item:focus-visible{outline:2px solid var(--ph-amber);outline-offset:-2px}
html body .ps-nav-item.ps-sub{padding-left:30px;font-size:var(--font-navigation);position:relative}
html body .ps-nav-item.ps-sub::before{content:'';position:absolute;left:15px;top:50%;width:8px;height:9px;margin-top:-9px;
  border-left:1px solid rgba(255,255,255,.3);border-bottom:1px solid rgba(255,255,255,.3)}
/* Things apps move onto the bar that are not plain buttons. */
html body .ps-bar .ps-actions .seg{display:inline-flex;height:30px;border:1px solid rgba(255,255,255,.24);border-radius:6px;overflow:hidden;flex:none}
html body .ps-bar .ps-actions .seg[hidden]{display:none}
html body .ps-bar .ps-actions .seg button{height:28px;border:0;border-radius:0;background:transparent;color:#b9c8de;padding:0 10px}
html body .ps-bar .ps-actions .seg button:is(.on,[aria-pressed=true]){background:rgba(255,255,255,.16);color:#fff;font-weight:600}
html body .ps-bar .ps-actions :is(div,span):not(.seg):not(.live):not(.ps-status){display:inline-flex;align-items:center;gap:8px;flex:none}
html body .ps-bar .ps-actions [hidden]{display:none!important}
/* A dropdown panel that belongs to a button on the bar (e.g. notifications) is a light
   surface again, whatever the bar's white-on-navy rules say. */
html body .ps-bar .ps-actions .ps-panel,html body .ps-bar .ps-actions .ps-panel *{color:var(--ph-ink)}
/* The page's main action (an app's "primary" button) is a shade stronger, never navy-on-navy. */
html body .ps-bar .ps-actions :is(.pri,.primary,.btn-primary,.gold){background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.5);font-weight:600}
/* Folder arrows drawn the same way in every app. Apps that print a ▶/▼ character in a
   .car element (Asset, Budget) get the shared chevron instead: the character is hidden
   and the arrow is a mask in the text colour. --ps-chev-r points right (rotated by the
   app when open); --ps-chev-d points down. */
:root{
  --ps-chev-r:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 2l3 3-3 3'/%3E%3C/svg%3E");
  --ps-chev-d:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 3.5l3 3 3-3'/%3E%3C/svg%3E")}
.ps-bar > .ps-brand:first-child{margin-left:10px}   /* no grid button (signed-out landing): the brand keeps its inset */
