/* ============================================================================
   34 ECB · CPMS — Bangladesh Army theme (deep green + gold), light & dark.
   Colour tokens are declared once and swapped per theme via [data-theme].
   Every component reads from tokens, so both themes stay in sync.
   ========================================================================== */

/* ── `hidden` means hidden ─────────────────────────────────────────────────
   The browser hides `[hidden]` with `display:none` from its own stylesheet, and
   ANY author rule that sets `display` beats it — author beats user-agent,
   whatever the specificity. `.badge{display:inline-flex}` is enough to un-hide
   a badge the template marked hidden, and the screen board relies on toggling
   exactly that attribute to show and clear its status pills.

   Stated once, here, rather than discovered one element at a time.
   `!important` because the point is to outrank a `display` declared later in
   the file. Nothing in this project overrides it with an inline style — the
   scripts toggle the attribute, never `style.display`. */
[hidden]{display:none !important}

/* Professional variable typeface, vendored locally (crisp + offline-safe). */
@font-face{
  font-family:'Inter';
  src:url('../fonts/inter-var.woff2') format('woff2');
  font-weight:100 900;font-style:normal;font-display:swap;
}

/* The Bangla face (SolaimanLipi) that used to sit here has been removed with
   the rest of the bilingual layer. The .woff2 files remain in static/fonts/ so
   the decision is reversible; nothing references them, so nothing fetches them.
   Inter above is now the only vendored face, and the only web font on any page. */

/* ============================================================================
   DESIGN TOKENS
   ----------------------------------------------------------------------------
   Everything downstream reads from these, so both themes stay in step and a
   palette change is an edit in one block rather than a search through 1,700
   lines. Three groups:

     · scale   — type, spacing, radius, elevation, motion. Theme-independent.
     · palette — the Bangladesh Army green and gold, as a tint ramp.
     · role    — what a surface, a border or a muted label resolves to *in this
                 theme*. Only these are redefined per theme.

   Contrast: every foreground/background pair below meets WCAG AA (4.5:1 for
   body text, 3:1 for large text and UI borders) in BOTH themes. The old light
   theme failed on --muted-2 and on every coloured badge; those are fixed here
   rather than patched at each call site.
   ========================================================================== */
:root{
  /* Inter, and then the generic. The intermediate fallbacks — Segoe UI,
     system-ui, Roboto — were there for a font that might not arrive; this one
     is vendored beside the stylesheet, so it always does. Naming five faces
     nobody will ever see only made it harder to tell what the site is set in. */
  --font:'Inter',sans-serif;

  /* ── Type scale. clamp() so a phone and a 4K panel both read correctly
        without a media query per size. ──────────────────────────────────── */
  --fs-xs:   0.72rem;
  --fs-sm:   0.8rem;
  --fs-md:   0.875rem;
  --fs-base: 0.94rem;
  --fs-lg:   clamp(1rem, 0.96rem + 0.2vw, 1.1rem);
  --fs-xl:   clamp(1.15rem, 1.05rem + 0.5vw, 1.4rem);
  --fs-2xl:  clamp(1.4rem, 1.2rem + 1vw, 2rem);
  --fs-3xl:  clamp(1.9rem, 1.4rem + 2.4vw, 2.9rem);

  /* ── Spacing. Page gutters shrink on a phone and grow on a wall. ──────── */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 20px;  --sp-6: 24px;  --sp-8: 32px;  --sp-10: 40px;
  --gutter: clamp(14px, 3.2vw, 36px);

  /* ── Radius ───────────────────────────────────────────────────────────── */
  --r-sm: 8px;  --r-md: 11px;  --r-lg: 14px;  --r-xl: 18px;  --r-pill: 999px;
  --radius: var(--r-xl);

  /* ── Touch. 44px is the smallest target a thumb hits reliably; every
        button, link-button, tab and form control is held to it below the
        pointer:coarse breakpoint. ───────────────────────────────────────── */
  --tap: 44px;
  --tap-sm: 40px;

  /* ── Chrome heights, exported so JS and other rules can offset by them ── */
  --topbar-h: 58px;
  --tabbar-h: 58px;
  --side-w: 264px;
  --rail-w: 76px;

  /* ── Safe areas. Zero on a desktop; the notch and the home indicator on a
        phone. Read them once here so no other rule repeats the env() dance. ─ */
  --safe-t: env(safe-area-inset-top, 0px);
  --safe-r: env(safe-area-inset-right, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);

  /* ── Motion ───────────────────────────────────────────────────────────── */
  --ease: cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
  --dur-1: .14s;  --dur-2: .22s;  --dur-3: .3s;
  --hero-glow: linear-gradient(135deg, rgba(224,164,74,.22), rgba(15,61,135,.12));
}

/* ── LIGHT (default) ──────────────────────────────────────────────────────
   Light is the default now: these screens are read in daylight, in offices and
   on site, far more often than in a dark room. Dark remains a real theme, not
   an inversion filter. */
:root,
:root[data-theme="light"]{
  color-scheme: light;

  /* Bangladesh Army green, as a ramp. --army is the interactive green;
     --army-2/-3 are the deeper values the sidebar and gradients use. */
  /* The brand ramp. Named `army` throughout because four thousand lines and
     nine templates refer to it; the VALUE is the deep navy of the reference
     design, the name is the slot it fills. Renaming the token would be a
     rename of every reference for no change on screen. */
  --army-50:   #eef5fd;
  --army-100:  #d8e7fa;
  --army-200:  #aecdf4;
  --army-300:  #6ea6ea;
  --army-400:  #2f70cf;
  --army:      #0f3d87;
  --army-2:    #0b2e68;
  --army-3:    #082246;

  /* Signature gold. On a white surface only --gold-ink is dark enough for
     text; --gold stays the decorative value for rules, bars and icons. */
  /* The highlight, now the reference design's amber badge rather than the
     signature gold. --gold-ink stays the only value dark enough for text on
     white; --gold remains decorative. */
  --gold-100:  #fdefd6;
  --gold-200:  #f8d69b;
  --gold:      #d8862a;
  --gold-2:    #b56d18;
  --gold-ink:  #8a5210;

  --red:       #b3121f;

  /* White. Not off-white, not a tint — the page is white and the depth comes
     from hairlines and shadows instead of from colour.
     --surface-2 and --surface-3 stay a whisper of grey-blue because they are
     the *fills*: hover rows, chips, inset panels. If those were white too they
     would vanish into the page and every control would lose its edge. */
  --bg-0:      #ffffff;
  --bg-1:      #ffffff;
  --surface:   #ffffff;
  --surface-2: #f5f8fc;
  --surface-3: #eaf0f7;
  --brd:       rgba(15,61,135,.16);
  --brd-soft:  rgba(11,37,69,.10);

  /* Ink re-mixed towards the ground it sits on. Contrast ratios re-checked
     against #ffffff, because a palette swap that quietly drops text to 3:1 is
     the usual way a redesign fails an accessibility audit. */
  --txt:        #16243a;   /* 13.6:1 on white */
  --txt-strong: #0b1b30;   /* 16.4:1 */
  --muted:      #4f6178;   /* 7.0:1 */
  --muted-2:    #64768d;   /* 5.0:1 */

  --ok:   #0d7a4f;
  --warn: #8a5a06;
  --bad:  #b3121f;
  /* Tinted grounds for badges and messages. Kept opaque rather than an alpha
     wash, so a badge reads the same on a card as it does on the page. */
  --ok-bg:   #e2f4ea;   --ok-brd:   #a9dcc2;
  --warn-bg: #fbf0d8;   --warn-brd: #e6c988;
  --bad-bg:  #fdeaec;   --bad-brd:  #f0b4ba;
  --info-bg: #e8f1fb;   --info-brd: #b7d0ee;

  /* Elevation: a light theme needs tight, tinted shadows — a black blur on a
     white page reads as dirt. */
  --e1: 0 1px 2px rgba(11,37,69,.06), 0 1px 1px rgba(11,37,69,.04);
  --e2: 0 2px 6px rgba(11,37,69,.07), 0 1px 2px rgba(11,37,69,.05);
  --e3: 0 8px 20px -6px rgba(11,37,69,.13), 0 2px 6px rgba(11,37,69,.06);
  --e4: 0 20px 44px -12px rgba(11,37,69,.20), 0 4px 12px rgba(11,37,69,.08);
  --shadow:    var(--e3);
  --shadow-sm: var(--e2);

  /* The reference design's ground: a cyan wash at the top-left corner falling
     into a pale blue page. Two radials rather than one so the corner has a
     tint and the body does not — a single flat gradient reads as a coloured
     rectangle, which is what it is. */
  /* A flat white page. The two radial washes that used to tint the corners are
     gone: asked for white, and a gradient is not white however faint it is. */
  --page-bg: var(--bg-0);
  --side-bg:   linear-gradient(180deg, #123f8a, #0a2550);
  --card-bg:   var(--surface);
  --card-blur: none;
  --thumb-bg:  #dde8f3;
  --input-bg:  #ffffff;
  --option-bg: #ffffff;
  --tr-hover:  rgba(15,61,135,.05);
  --scrim:     rgba(8,20,40,.45);
  --focus:     var(--army-400);
}

/* ── DARK ─────────────────────────────────────────────────────────────────
   Not an inverted light theme: the greens lighten so they still read as
   *green* against a near-black ground, and the gold warms up, because a dark
   gold on a dark page is mud. */
:root[data-theme="dark"]{
  color-scheme: dark;

  --army-50:   #0b1626;
  --army-100:  #102138;
  --army-200:  #16304f;
  --army-300:  #1d4a7d;
  --army-400:  #2f6cb8;
  --army:      #4a90e2;
  --army-2:    #2b6bb5;
  --army-3:    #1a4682;

  --gold-100:  #3a2a10;
  --gold-200:  #6b4a18;
  --gold:      #eaa64d;
  --gold-2:    #cf8a2c;
  --gold-ink:  #f5c88a;

  --red:       #e05561;

  --bg-0:      #060d18;
  --bg-1:      #0a1524;
  --surface:   #0f1b2c;
  --surface-2: #16253a;
  --surface-3: #1d2f49;
  --brd:       rgba(74,144,226,.22);
  --brd-soft:  rgba(255,255,255,.10);

  --txt:        #e6eef8;
  --txt-strong: #ffffff;
  --muted:      #9fb0c6;   /* 7.6:1 on --surface */
  --muted-2:    #8598b1;   /* 5.6:1 on --surface */

  --ok:   #4dd694;
  --warn: #f2bf5e;
  --bad:  #ff7b83;
  --ok-bg:   #0f2e21;   --ok-brd:   #1e5a3f;
  --warn-bg: #33280d;   --warn-brd: #6d5518;
  --bad-bg:  #34161a;   --bad-brd:  #6d2b31;
  --info-bg: #13233a;   --info-brd: #2c4568;

  --e1: 0 1px 2px rgba(0,0,0,.45);
  --e2: 0 2px 8px rgba(0,0,0,.5);
  --e3: 0 10px 26px -8px rgba(0,0,0,.6), 0 2px 6px rgba(0,0,0,.4);
  --e4: 0 24px 54px -14px rgba(0,0,0,.72), 0 6px 16px rgba(0,0,0,.45);
  --shadow:    var(--e3);
  --shadow-sm: var(--e2);

  --page-bg:
    radial-gradient(1100px 560px at 8% -12%, rgba(24,110,74,.28), transparent 60%),
    radial-gradient(1000px 520px at 100% -8%, rgba(226,188,76,.08), transparent 55%),
    linear-gradient(165deg, var(--bg-0), var(--bg-1));
  --side-bg:   linear-gradient(180deg, #0d1c30, #060d18);
  --card-bg:   var(--surface);
  --card-blur: none;
  --thumb-bg:  #0e2a20;
  --input-bg:  #0c1c15;
  --option-bg: #0e241b;
  --tr-hover:  rgba(226,188,76,.06);
  --scrim:     rgba(0,0,0,.62);
  --focus:     var(--gold);
}

*{box-sizing:border-box}
html{
  -webkit-text-size-adjust:100%;
  /* Not `scroll-behavior:smooth` globally: it makes every in-page jump — an
     anchor, a form error, restoring position on Back — animate, which on a
     phone reads as lag. Opted into per interaction instead. */
  scroll-behavior:auto;
}
@media (prefers-reduced-motion:no-preference){
  html:focus-within{scroll-behavior:smooth}
}
body{
  margin:0;
  font-family:var(--font);
  font-size:15px;
  line-height:1.5;
  letter-spacing:-0.006em;
  color:var(--txt);
  /* NOT `fixed`: background-attachment:fixed forces the compositor to repaint
     the whole background every scroll frame on mobile Safari and Chrome, and
     it is the single biggest cause of scroll jank on a page like this. The
     gradient is fixed to the *viewport* with a pseudo-element instead, which
     stays on the GPU. */
  background:var(--bg-0);
  min-height:100vh;
  min-height:100dvh;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  font-feature-settings:"cv11","ss01";
  overflow-x:hidden;
}
body::before{
  content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;
  background:var(--page-bg);
}
a{color:inherit;text-decoration:none}
::selection{background:var(--army-200);color:var(--txt-strong)}
h1,h2,h3,.num,.v{letter-spacing:-0.02em}
h1,h2,h3,h4{line-height:1.16}
/* Tabular figures keep numeric columns + KPIs aligned and crisp. */
.num,.v,table td,table th,.kpi,.stat,.progress + .meta{font-variant-numeric:tabular-nums}

/* A visible, consistent focus ring everywhere. Keyboard users get it; a mouse
   click does not, because :focus-visible already makes that distinction. */
:focus-visible{outline:2px solid var(--focus);outline-offset:2px;border-radius:4px}

/* Long project names and pasted URLs must not widen the page. */
h1,h2,h3,h4,p,td,th,li,dd,dt,.title,.sp-name{overflow-wrap:break-word}

/* Media never overflows its column. */
img,svg,video,canvas{max-width:100%;height:auto}

/* Skip link: the first thing a keyboard or screen-reader user reaches, and
   invisible until it is focused. Without it, every page starts with 50-odd
   sidebar links before the content. */
.skip-link{
  position:fixed;top:0;left:50%;transform:translate(-50%,-140%);
  z-index:200;padding:12px 22px;border-radius:0 0 var(--r-md) var(--r-md);
  background:var(--army);color:#fff;font-weight:700;font-size:var(--fs-md);
  box-shadow:var(--e4);transition:transform var(--dur-2) var(--ease-out);
}
.skip-link:focus{transform:translate(-50%,0)}

/* Body scroll lock, used by the drawer and the lightbox. `position:fixed` is
   the only form iOS Safari honours; app.js stashes and restores the offset. */
body.scroll-locked{position:fixed;left:0;right:0;width:100%;overflow:hidden}
body.scroll-locked-simple{overflow:hidden}

/* ============================================================================
   SHELL / LAYOUT
   ----------------------------------------------------------------------------
   One structure, three presentations, all driven by width:

     ≥1024px  sidebar is a permanent column; no drawer, no tab bar
     <1024px  sidebar becomes an off-canvas drawer behind a hamburger
      <768px  a bottom tab bar appears with the five primary destinations

   The drawer rules are gated on `html.js`, which app.js sets. With JavaScript
   off there is nothing to open the drawer with, so the sidebar stays a plain
   static column instead of becoming permanently invisible.
   ========================================================================== */
.shell{
  display:flex;
  min-height:100vh;
  min-height:100dvh;   /* dvh excludes the mobile URL bar, so no 100vh jump */
}
.side{
  width:var(--side-w);flex:0 0 var(--side-w);
  padding:22px 16px;
  padding-top:calc(22px + var(--safe-t));
  padding-left:calc(16px + var(--safe-l));
  background:
    radial-gradient(360px 220px at 100% -10%, rgba(224,164,74,.22), transparent 70%),
    linear-gradient(180deg, #0f3d87 0%, #082b20 100%);
  border-right:1px solid rgba(255,255,255,.12);
  position:sticky;top:0;height:100vh;height:100dvh;color:#eaf3ee;
  display:flex;flex-direction:column;
  overflow:hidden;
  z-index:60;
  box-shadow:inset -1px 0 0 rgba(255,255,255,.06);
}
.brand{
  font-weight:800;font-size:1.02rem;letter-spacing:.2px;color:#fff;
  display:flex;align-items:center;gap:12px;padding-bottom:16px;
  border-bottom:1px solid rgba(226,188,76,.25);
  min-width:0;
}
.brand-logo{
  width:46px;height:46px;flex:0 0 46px;object-fit:contain;border-radius:50%;
  background:#fff;padding:3px;border:1.5px solid rgba(224,164,74,.55);
  box-shadow:0 2px 10px rgba(0,0,0,.25);
}
/* Two lines under the emblem: the mark, and what the system is.
   `min-width:0` so the second line truncates inside the 250px column instead of
   forcing the sidebar wider, and `1.15` because two lines at 1.2 sat a shade
   loose against a 46px badge. */
.brand-text{display:flex;flex-direction:column;line-height:1.15;min-width:0}
.brand-text strong{font-weight:800;letter-spacing:.3px}
.brand-text small{
  color:#b6cabd;font-weight:500;font-size:.62rem;margin-top:3px;
  letter-spacing:.5px;text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.nav{
  margin-top:20px;display:flex;flex-direction:column;gap:4px;
  flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding-right:4px;margin-right:-4px;
  scrollbar-width:thin;scrollbar-color:rgba(224,164,74,.35) transparent;
}
.nav::-webkit-scrollbar{width:6px}
.nav::-webkit-scrollbar-thumb{background:rgba(224,164,74,.35);border-radius:99px}
.nav a,
.nav-group > summary,
.side-public{
  padding:12px 14px;border-radius:12px;color:#c4d6cc;font-size:.95rem;font-weight:600;
  line-height:1.2;letter-spacing:.01em;display:flex;gap:11px;align-items:center;
  min-height:44px;transition:all .16s ease;border:1px solid transparent;
}
.nav a:hover,
.nav-group > summary:hover,
.side-public:hover{background:rgba(255,255,255,.08);color:#fff;transform:translateX(3px)}
.nav a.active,
.nav-group > summary.active{
  background:linear-gradient(90deg, rgba(224,164,74,.24), rgba(224,164,74,.08));
  color:#fff;border-color:rgba(224,164,74,.4);
  box-shadow:inset 3px 0 0 var(--gold);
}
.nav a.active:hover,
.nav-group > summary.active:hover{transform:none}

/* ── Interface icons ───────────────────────────────────────────────────────
   The same inline-SVG system as the sidebar, for buttons, headings and table
   cells. Sized in `em` rather than px, which is the whole point of the class:
   an icon in a button is part of a word, so it has to grow with the text —
   including when a wall panel is drawn at 175% by `setTextZoom`, which scales
   text and would have left a px-sized icon behind at its original size.

   `currentColor` for the same reason: an icon on a gold button is gold, on a
   ghost button is the body colour, and in dark mode follows both, with nothing
   to declare per variant.

   These replaced two hundred literal emoji characters. See core/navicons.py —
   an emoji is a font lookup, and the Android WebViews this fleet runs on have
   no glyph for most of them. */
.ic{
  width:1.05em;height:1.05em;flex:0 0 auto;
  stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  vertical-align:-.16em;
}
/* Inside a button the icon sits against a word, so it needs the gap a space
   used to provide — the emoji carried its own. */
.btn .ic{margin-right:.4em}
.btn .ic:only-child{margin-right:0}

/* Consistent inline-SVG icon system for every sidebar entry. Icons inherit the
   link colour via currentColor, so they track hover / active / theme for free. */
.nav-ic{
  width:19px;height:19px;flex:0 0 19px;
  stroke:currentColor;fill:none;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
  opacity:.72;transition:opacity .16s ease,transform .16s ease;
}
.nav a:hover .nav-ic,
.nav-group > summary:hover .nav-ic,
.side-public:hover .nav-ic{opacity:1}
.nav a.active .nav-ic,
.nav-group > summary.active .nav-ic{opacity:1;color:var(--gold)}

/* Small uppercase section dividers group the links into Workspace / Admin. */
.nav-section{
  margin:16px 13px 5px;font-size:.62rem;font-weight:800;letter-spacing:1.4px;
  text-transform:uppercase;color:#7f9a8d;user-select:none;
}
.nav-section:first-child{margin-top:4px}

/* The Projects <summary> holds an icon + label on the left, count on the right. */
.nav-label{display:inline-flex;align-items:center;gap:11px;min-width:0}
.nav-label-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.nav-external .ext-mark{margin-left:auto;flex:none;opacity:.55;font-size:.82rem}


.main{
  flex:1 1 auto;min-width:0;width:100%;max-width:1620px;
  padding:20px var(--gutter) 0;
  padding-right:calc(var(--gutter) + var(--safe-r));
  display:flex;flex-direction:column;min-height:100vh;min-height:100dvh;
}
.main-content{flex:1 0 auto;min-width:0}
/* Any wide table lives inside one of these so it scrolls itself instead of
   forcing the whole shell wide (which used to shove the sticky sidebar off). */
.table-wrap{
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  /* Momentum scrolling on a phone should stop at this box rather than
     handing the gesture on to the page behind it. */
  overscroll-behavior-x:contain;
  scrollbar-width:thin;scrollbar-color:var(--army-300) transparent;
}
.table-wrap::-webkit-scrollbar{height:8px}
.table-wrap::-webkit-scrollbar-thumb{background:var(--army-300);border-radius:var(--r-pill)}
.main > .app-footer{margin-top:34px}

/* ── Top app bar ──────────────────────────────────────────────────────────
   Sticky on every size. On a phone it is the app bar: hamburger, page title,
   account. The account menu and theme button never wrap below the title —
   wrapping doubled the bar's height and ate a third of a small screen. */
.topbar{
  display:flex;align-items:center;gap:10px;
  margin-bottom:20px;
  position:sticky;top:10px;z-index:40;
  min-height:var(--topbar-h);
  padding:10px 14px;border-radius:calc(var(--r-lg) + 2px);
  background:var(--surface);
  border:1px solid var(--brd);box-shadow:var(--e3);
  backdrop-filter:blur(16px) saturate(1.4);
  -webkit-backdrop-filter:blur(16px) saturate(1.4);
}
.topbar h1{
  font-size:var(--fs-xl);margin:0;font-weight:800;color:var(--txt-strong);
  letter-spacing:-.02em;display:flex;align-items:center;
  flex:1 1 auto;min-width:0;
  /* One line, ellipsised. A two-line heading on a phone pushes the whole page
     down by 30px on every screen. */
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.topbar h1::before{
  content:"";display:inline-block;width:5px;height:20px;border-radius:3px;
  background:linear-gradient(var(--gold),var(--army));margin-right:11px;
  flex:0 0 5px;
}
.topbar-tools{display:flex;gap:8px;align-items:center;flex:0 0 auto}

/* ── Hamburger: only below the drawer breakpoint ──────────────────────────── */
.drawer-btn{
  display:none;flex:0 0 auto;
  width:var(--tap);height:var(--tap);border-radius:var(--r-md);
  align-items:center;justify-content:center;cursor:pointer;
  background:transparent;border:1px solid transparent;color:var(--txt);
  margin-left:-6px;
  transition:background var(--dur-1) ease,border-color var(--dur-1) ease;
}
.drawer-btn svg{width:22px;height:22px;stroke:currentColor;fill:none;
  stroke-width:2;stroke-linecap:round}
.drawer-btn:hover{background:var(--surface-2);border-color:var(--brd)}
.drawer-btn:active{background:var(--surface-3)}

/* ── Drawer scrim ─────────────────────────────────────────────────────────
   Painted only while the drawer is open. `hidden` is toggled by app.js one
   frame before the class, so the fade actually has a start state. */
.nav-scrim{
  position:fixed;inset:0;z-index:55;
  background:var(--scrim);
  backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);
  opacity:0;transition:opacity var(--dur-2) ease;
}
.drawer-open .nav-scrim{opacity:1}

/* ── Drawer close button: only inside the drawer ──────────────────────────── */
.drawer-close{
  display:none;flex:none;
  width:38px;height:38px;border-radius:var(--r-md);
  align-items:center;justify-content:center;cursor:pointer;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.14);
  color:#dcebe3;
}
.drawer-close:hover{background:rgba(255,255,255,.16);color:#fff}
.drawer-close .nav-ic{opacity:1;width:18px;height:18px;flex-basis:18px}

/* ── Bottom tab bar: phones only ──────────────────────────────────────────
   Five destinations, each a full-height target with an icon over a label. It
   is `position:fixed` so it stays put while the page scrolls — which is what
   distinguishes an app from a page — and padded by the home-indicator inset. */
.tabbar{
  display:none;
  position:fixed;left:0;right:0;bottom:0;z-index:50;
  padding-bottom:var(--safe-b);
  background:linear-gradient(180deg, color-mix(in srgb, var(--surface) 96%, transparent), color-mix(in srgb, var(--surface-2) 92%, transparent));
  backdrop-filter:blur(20px) saturate(1.5);
  -webkit-backdrop-filter:blur(20px) saturate(1.5);
  border-top:1px solid var(--brd);
  box-shadow:0 -10px 30px rgba(11,37,69,.08);
}
.tabbar a,
.tabbar button{
  flex:1 1 0;min-width:0;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;padding:7px 2px 6px;min-height:var(--tabbar-h);
  background:none;border:0;cursor:pointer;font-family:inherit;
  color:var(--muted);
  -webkit-tap-highlight-color:transparent;
  transition:color var(--dur-1) ease;
}
.tabbar svg{
  width:21px;height:21px;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform var(--dur-2) var(--ease-out);
}
.tabbar span{
  font-size:.63rem;font-weight:700;letter-spacing:.2px;line-height:1;
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.tabbar .on,
.tabbar a:active,
.tabbar button:active{color:var(--army)}
:root[data-theme="dark"] .tabbar .on{color:var(--gold)}
/* The active tab carries a short bar at the top of its cell — the standard
   native affordance, and legible without relying on colour alone. */
.tabbar .on{position:relative}
.tabbar .on::before{
  content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:26px;height:3px;border-radius:0 0 3px 3px;background:currentColor;
}
.tabbar .on svg{transform:translateY(-1px) scale(1.06)}

/* ── Icon button (theme) ──────────────────────────────────────────────────
   Sun and moon are both in the DOM; CSS shows the one that names the action.
   Light theme offers the moon, dark theme offers the sun. */
.icon-btn{
  width:42px;height:42px;border-radius:50%;border:1px solid var(--brd);
  background:var(--card-bg);color:var(--txt);cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;flex:0 0 42px;
  box-shadow:var(--shadow-sm);
  transition:transform .25s ease,background .2s,border-color .2s,color .2s;
}
.icon-btn:hover{border-color:var(--gold);background:var(--surface-2);color:var(--gold)}
.icon-btn:active{transform:scale(.94)}
.icon-btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.icon-btn svg{
  width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.ic-moon{display:none}
[data-theme="dark"] .ic-sun{display:none}
[data-theme="dark"] .ic-moon{display:block}

/* ── Account menu ─────────────────────────────────────────────────────────
   <details> so it opens without JavaScript and closes on Escape natively.  */
.user-menu{position:relative}
.user-menu > summary{
  list-style:none;cursor:pointer;
  display:flex;gap:11px;align-items:center;
  background:var(--card-bg);border:1px solid var(--brd);
  padding:5px 12px 5px 6px;border-radius:40px;
  box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur);
  transition:border-color .18s ease,box-shadow .18s ease;
}
.user-menu > summary::-webkit-details-marker{display:none}
.user-menu > summary:hover{border-color:var(--gold)}
.user-menu > summary:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.user-menu[open] > summary{border-color:var(--gold);box-shadow:var(--shadow)}

.avatar{
  width:36px;height:36px;flex:0 0 36px;border-radius:50%;
  display:flex;align-items:center;justify-content:center;
  font-weight:800;font-size:.82rem;letter-spacing:.5px;color:#fff;
  background:linear-gradient(135deg,var(--army),var(--army-2));
  border:1.5px solid rgba(224,164,74,.65);box-shadow:0 2px 8px rgba(0,0,0,.2);
}
.avatar.lg{width:46px;height:46px;flex-basis:46px;font-size:1rem}

.user-meta{display:flex;flex-direction:column;line-height:1.2;min-width:0;max-width:190px}
.user-name{font-weight:700;font-size:.86rem;color:var(--txt-strong);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.user-role{font-size:.68rem;color:var(--muted);letter-spacing:.2px;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.um-caret{
  width:15px;height:15px;flex:none;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;color:var(--muted);
  transition:transform .2s ease;
}
.user-menu[open] .um-caret{transform:rotate(180deg)}

.um-panel{
  position:absolute;top:calc(100% + 10px);inset-inline-end:0;z-index:60;
  min-width:262px;padding:8px;border-radius:14px;
  background:var(--card-bg);border:1px solid var(--brd);
  box-shadow:var(--shadow);backdrop-filter:blur(14px);
  animation:umIn .16s cubic-bezier(.22,.61,.36,1);
}
@keyframes umIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}

.um-head{
  display:flex;gap:12px;align-items:center;padding:10px 10px 12px;
  border-bottom:1px solid var(--brd-soft);margin-bottom:6px;
}
.um-head > div{display:flex;flex-direction:column;gap:3px;min-width:0}
.um-head small{font-size:.66rem;color:var(--muted);text-transform:uppercase;letter-spacing:.7px}
.um-head strong{font-size:.92rem;color:var(--txt-strong);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.um-head .badge{align-self:flex-start;margin-top:2px}

/* Sign-out is a <button> inside a POST form, not a link — see base.html. It
   must be indistinguishable from the links above it. */
.um-form{margin:0}
.um-form .um-item{width:100%;background:none;border:0;font:inherit;cursor:pointer;text-align:left}

.um-item{
  display:flex;align-items:center;gap:10px;
  padding:10px 11px;border-radius:10px;font-size:.86rem;color:var(--txt);
  transition:background .15s ease,color .15s ease;
}
.um-item .nav-ic{width:17px;height:17px;flex-basis:17px;opacity:.75}
.um-item:hover{background:var(--surface-2);color:var(--txt-strong)}
.um-item:hover .nav-ic{opacity:1}
.um-item:focus-visible{outline:2px solid var(--gold);outline-offset:-2px}
.um-item.danger{color:var(--bad)}
.um-item.danger:hover{background:color-mix(in srgb,var(--bad) 12%,transparent)}

@media (prefers-reduced-motion:reduce){
  .um-panel{animation:none}
  .icon-btn,.um-caret{transition:none}
}

@media (max-width:620px){
  .topbar h1{font-size:1.2rem}
  .user-meta,.um-caret{display:none}
  .user-menu > summary{padding:5px}
  /* The panel stays anchored to its trigger's RIGHT edge on a phone.
     Left-anchoring it here used to push a 262px panel off a 360px screen from
     x≈304 — and `body{overflow-x:hidden}` clips it with no way to scroll back,
     so Sign out (which exists nowhere else in the shell) became a blind sliver.
     The width cap is for the few sub-288px devices left. */
  .um-panel{min-width:min(262px, calc(100vw - 24px))}
  .dashboard-hero{flex-direction:column;align-items:flex-start}
  .hero-actions{width:100%;justify-content:flex-start}
}

/* ── Surfaces ────────────────────────────────────────────────────────────── */
.glass{
  background:linear-gradient(135deg, color-mix(in srgb, var(--surface) 96%, transparent), color-mix(in srgb, var(--surface-2) 86%, transparent));
  border:1px solid var(--brd);border-radius:var(--radius);
  backdrop-filter:var(--card-blur);box-shadow:var(--shadow);
}

/* ── Card padding ──────────────────────────────────────────────────────────
   Three densities every panel in the application draws from:

     .pad        a panel somebody reads — a detail block, a form, a notice
     .pad-sm     a card in a grid, where the gap between cards does the work
     .pad-flush  a wrapper around a table that draws its own edges

   A class rather than the 46 copies of `style="padding:22px"` and 34 of
   `style="padding:18px"` that were typed into the templates by hand, and not
   only because repetition is untidy. **An inline style cannot be reached by a
   media query** without `!important`, so on a phone every one of those cards
   kept its desktop padding — 22px of gutter either side of a 320px screen, on
   a page made of six stacked cards, which is most of the width gone before any
   content is drawn. The step at the bottom of this file is possible only
   because the value lives here.

   Not folded into `.glass` itself: a dozen components — the KPI tiles, the
   project cards, the notification rows — are `.glass` *and* carry geometry of
   their own, and a padding on the shared class would win over theirs on source
   order and flatten all of them. */
.pad{padding:22px}
.pad-sm{padding:18px}
.pad-flush{padding:6px 6px 8px}

/* A panel's first heading sits against the padding rather than on top of its
   own margin — which is what `style="margin-top:0"` was doing, 25 times, and
   `style="margin:0"` another 30. Declared on `.glass` as well as the padding
   classes: a handful of cards set their padding some other way, and the rule
   is about the *card*, not about which class supplied the number. */
.glass > :first-child, .pad > :first-child, .pad-sm > :first-child{margin-top:0}
.glass > :last-child, .pad > :last-child, .pad-sm > :last-child{margin-bottom:0}

/* ── KPIs ────────────────────────────────────────────────────────────────── */
.kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-bottom:24px}
.dashboard-hero{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:20px 22px;margin-bottom:20px;position:relative;overflow:hidden;
  background:
    radial-gradient(360px 220px at 100% 0%, rgba(224,164,74,.16), transparent 70%),
    linear-gradient(135deg, color-mix(in srgb, var(--surface) 94%, transparent), color-mix(in srgb, var(--surface-2) 88%, transparent));
}
.dashboard-hero::before{content:"";position:absolute;inset:0;background:var(--hero-glow);pointer-events:none}
.dashboard-hero > *{position:relative;z-index:1}
.hero-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 10px;border-radius:999px;background:rgba(15,61,135,.10);color:var(--army);font-weight:700;font-size:.72rem;text-transform:uppercase;letter-spacing:.8px;border:1px solid rgba(15,61,135,.14)}
.dashboard-hero h2{margin:8px 0 4px;font-size:1.2rem;color:var(--txt-strong)}
.dashboard-hero p{margin:0;color:var(--muted);font-size:.92rem;max-width:680px}
.hero-actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-end}
/* ── The headline counters ────────────────────────────────────────────────
   Three figures, and they are the first thing on the page — so they carry a
   coloured top edge to tell them apart and nothing else. The blurred amber
   disc that used to sit behind each one was decoration from the green palette;
   on a white page it read as a smudge rather than a highlight. */
.kpi{
  padding:20px 22px;position:relative;overflow:hidden;
  border-radius:var(--r-xl);
  transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;
}
.kpi:hover{border-color:var(--army-200)}
.kpi .label{color:var(--muted);font-size:.78rem;text-transform:uppercase;letter-spacing:1.2px;font-weight:600}
.kpi .num{
  font-size:2.6rem;font-weight:800;margin-top:8px;line-height:1;
  letter-spacing:-.02em;color:var(--army);
}
.kpi.a{border-top:3px solid var(--army)}
.kpi.b{border-top:3px solid var(--ok)}
.kpi.c{border-top:3px solid var(--gold)}

/* ── Dashboard utilities ──────────────────────────────────────────────────
   These replace inline `style=` attributes that were repeated across the
   dashboard template. Beyond the tidiness: an inline style cannot be
   overridden by a media query without `!important`, so every one of them was a
   value the responsive rules below could not reach. */

/* A coloured top edge that means "look at this one". */
.stat-flag-accent{border-top:3px solid var(--accent)}
.stat-flag-warn{border-top:3px solid var(--warn)}
.stat-flag-bad{border-top:3px solid var(--bad)}

.stat-row.spaced{margin-bottom:18px}

/* ── "What a screen plays", as ticks ───────────────────────────────────────
   Two boxes — Photographs, Video — in place of a three-option dropdown whose
   phrasings the operator had to translate their intention into. Tick both and a
   project with mixed media plays all of it.

   Django renders a CheckboxSelectMultiple as a <ul>; it is given the same chip
   treatment as the role and permission pickers so the page has one idea of what
   a tickable option looks like. */
.ms-ticks-field{grid-column:1/-1}
.ms-ticks-label{
  display:block;margin-bottom:8px;color:var(--muted);
  font-size:var(--fs-xs);text-transform:uppercase;letter-spacing:.6px;font-weight:700;
}
ul.media-ticks{display:flex;gap:10px;flex-wrap:wrap;list-style:none;margin:0;padding:0}
ul.media-ticks li{margin:0}
ul.media-ticks label{
  display:inline-flex;align-items:center;gap:9px;cursor:pointer;
  padding:10px 16px;border-radius:999px;
  border:1px solid var(--brd);background:var(--surface-2);
  font-size:var(--fs-md);font-weight:600;
  transition:border-color .16s ease,background .16s ease;
  /* A thumb-sized target: this is set on a tablet at a screen as often as at a
     desk. */
  min-height:var(--tap,40px);
}
ul.media-ticks label:hover{border-color:var(--army-200)}
ul.media-ticks input{accent-color:var(--army)}
ul.media-ticks input:checked + span,
ul.media-ticks label:has(input:checked){border-color:var(--army);background:var(--surface)}

/* "Add a preset" on the display-accounts board: a name and a button, on one
   line, widening to fill whatever the row has left. */
.sb-new-preset{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.sb-new-preset input[type="text"]{flex:1 1 260px;min-width:0}
.sb-new-preset .sa-help{flex:1 1 100%;margin:0}

/* Tick all / clear all, above the project-scope picker. */
.ms-bulk{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 12px}
.ms-bulk [data-scope-count]{font-size:.82rem}

/* "3 unread of 41", in the notification filter bar. */
.notif-tally{margin-right:auto;color:var(--muted);font-size:.86rem}
.notif-tally strong{color:var(--txt-strong);font-size:1.05rem}

/* The alert strip. `.stat` is `flex:1` because the finance row shares four
   figures across the width — but this row holds one tile, and one tile at
   `flex:1` is a single box drawn the full width of the page with a number in
   the corner of it. Here the tile takes the width it needs. */
.stat-row.alerts .stat{flex:0 1 260px}

/* `.section-title` is a flex row, so the `float:right` these links carried was
   silently ignored — floats do not apply to flex items, and the buttons simply
   sat next to the heading. `margin-left:auto` is how a flex item is pushed to
   the far edge. */
.section-action{margin-left:auto}

.card-progress{margin-top:8px}

/* ── Cards / grid ────────────────────────────────────────────────────────── */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:20px}
/* "No ongoing projects in your scope." — a sentence, and a grid item like any
   other unless told otherwise, so it was being laid out inside one 240px column
   and wrapping over three lines against the left edge. It is the first thing a
   newly created account sees, before any project has been assigned to it. */
.grid-empty{grid-column:1/-1;margin:0}
.card{
  overflow:hidden;transition:transform .18s ease,box-shadow .18s ease, border-color .18s ease;
  background:linear-gradient(135deg, color-mix(in srgb, var(--surface) 96%, transparent), color-mix(in srgb, var(--surface-2) 86%, transparent));border:1px solid var(--brd);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur);
}
.card:hover{transform:translateY(-5px);box-shadow:var(--shadow);border-color:rgba(224,164,74,.4)}
.card .thumb{
  height:168px;background:var(--thumb-bg) center/cover no-repeat;position:relative;
  display:flex;align-items:center;justify-content:center;color:var(--muted);font-size:.8rem;
}
.card .thumb::after{
  content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 55%,rgba(7,20,42,.35));
  pointer-events:none;
}
/* The status pill on a card thumbnail.
   Declared once. There used to be a second `.card-tag` block further down that
   set `right` without clearing this one's `left`, and an absolutely positioned
   box with BOTH offsets and `width:auto` stretches to fill the gap — so every
   tag rendered as a full-width bar across the top of the photograph instead of
   a chip. `right:auto` is stated rather than assumed for the same reason. */
.card-tag{
  position:absolute;top:10px;left:10px;right:auto;z-index:2;
  padding:5px 10px;border-radius:999px;
  background:rgba(8,20,42,.82);color:var(--gold);
  font-size:.68rem;font-weight:700;letter-spacing:.5px;
  border:1px solid rgba(224,164,74,.4);
  backdrop-filter:blur(8px);
}
/* A card that opens the lightbox. It is a real keyboard target (role=button,
   tabindex=0), so it needs a focus ring as well as a pointer. */
.card.is-tappable{cursor:pointer;-webkit-tap-highlight-color:transparent}
.card.is-tappable:focus-visible{outline:2px solid var(--focus);outline-offset:3px}
.card .body{padding:14px 16px}
.card .title{font-weight:700;font-size:.96rem;line-height:1.35;color:var(--txt-strong)}
.card .meta{color:var(--muted);font-size:.78rem;margin-top:7px;display:flex;flex-wrap:wrap;gap:6px 12px}

.section-title{
  margin:28px 0 14px;font-size:.82rem;color:var(--muted);text-transform:uppercase;
  letter-spacing:1.4px;font-weight:700;display:flex;align-items:center;gap:10px;
}
.section-title::before{content:"";width:22px;height:2px;background:linear-gradient(90deg,var(--gold),var(--army));border-radius:2px}

/* ── Tables ───────────────────────────────────────────────────────────────
   Every table in the project sits inside a `.table-wrap`, which scrolls itself
   horizontally rather than letting a wide table force the whole page wide. On
   a phone a horizontally-scrolling table is a *deliberate* choice: reflowing
   ten columns into stacked cards works for a roster (see `.company-table`) but
   destroys a numeric table where the comparison between rows is the point. */
table{width:100%;border-collapse:collapse;font-size:var(--fs-md)}
th,td{padding:12px 13px;text-align:left;border-bottom:1px solid var(--brd-soft)}
th{color:var(--muted);text-transform:uppercase;font-size:var(--fs-xs);letter-spacing:.6px;font-weight:700}
tbody tr{transition:background .12s ease}
tr:hover td{background:var(--tr-hover)}
td.right,th.right{text-align:right}
td.center,th.center{text-align:center}
/* A key/value table's label column: a proportion, never a pixel count. */
th.col-label{width:34%;min-width:110px}

/* Inside a scroller a table must keep a workable minimum, or the browser
   squeezes columns to one character per line instead of letting it scroll. */
.table-wrap > table{min-width:560px}
.table-wrap > table.narrow{min-width:0}

@media (max-width:767.98px){
  th,td{padding:10px 11px}
  /* A scrollable table needs to *look* scrollable, or nobody swipes it. */
  .table-wrap{
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 22px),transparent);
    mask-image:linear-gradient(90deg,#000 calc(100% - 22px),transparent);
  }
}

/* ════════════════════════════════════════════════════════════════════════
   FORM CONTROLS
   ------------------------------------------------------------------------
   Two rules govern everything here, and both are about phones:

   1. A control's font-size must be ≥16px on a touch device. Below that, iOS
      Safari zooms the whole page in when the field is focused and does not
      zoom back out — the single most common "the form is broken on my phone"
      report there is. Desktop keeps the tighter 14px.

   2. `min-width` on a control is a horizontal-overflow bug waiting to happen.
      The old rule set `min-width:150px` on every input; three of them in a
      flex row on a 320px screen forced the page 130px wide and the whole
      layout scrolled sideways. Controls now default to `min-width:0` and
      grow from their container instead.
   ════════════════════════════════════════════════════════════════════════ */
.filters{
  display:flex;gap:12px;flex-wrap:wrap;align-items:end;
  margin-bottom:20px;padding:16px 18px;
}
/* `flex:0 0 auto`, NOT `1 1 200px`. A `.field` sits in two kinds of container:
   a horizontal `.filters` bar, where it should share the row, and a vertical
   form, where `flex-grow:1` makes every field stretch to fill the column and
   leaves a screen-height gap between each label and the next. Growing is
   therefore opted into by the row containers below, not assumed here. */
.field{display:flex;flex-direction:column;gap:6px;min-width:0;flex:0 0 auto}
.filters > .field,
.form-row > .field,
.cs-advanced > .field{flex:1 1 200px}
.field label,
.form-label{
  font-size:var(--fs-xs);color:var(--muted);text-transform:uppercase;
  letter-spacing:.6px;font-weight:700;
}

.inp,
select,
textarea,
input[type=text],input[type=date],input[type=datetime-local],input[type=time],
input[type=password],input[type=number],input[type=email],input[type=search],
input[type=tel],input[type=url]{
  width:100%;min-width:0;max-width:100%;
  background:var(--input-bg);border:1px solid var(--brd);color:var(--txt);
  padding:10px 13px;border-radius:var(--r-md);
  font-size:var(--fs-base);line-height:1.4;font-family:inherit;
  outline:none;
  transition:border-color var(--dur-1) ease,box-shadow var(--dur-1) ease,
             background var(--dur-1) ease;
  /* Let the browser paint native controls (date pickers, select arrows) in the
     right theme instead of a white widget on a dark field. */
  color-scheme:inherit;
}
textarea{min-height:96px;resize:vertical}
select{
  /* A custom chevron, so the control looks the same in every browser. It is a
     data: URI rather than a file so it costs no request and cannot 404. */
  appearance:none;-webkit-appearance:none;
  padding-right:38px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23718b7e' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;
  background-position:right 12px center;
  background-size:17px;
}
select option{background:var(--option-bg);color:var(--txt)}

.inp:hover,select:hover,textarea:hover,
input:hover:not([type=checkbox]):not([type=radio]):not([type=file]){
  border-color:var(--army-300);
}
.inp:focus,select:focus,textarea:focus,
input:focus:not([type=checkbox]):not([type=radio]){
  border-color:var(--army);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--army) 22%,transparent);
}
:root[data-theme="dark"] .inp:focus,
:root[data-theme="dark"] select:focus,
:root[data-theme="dark"] textarea:focus,
:root[data-theme="dark"] input:focus:not([type=checkbox]):not([type=radio]){
  border-color:var(--gold);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--gold) 22%,transparent);
}
/* An invalid field must be distinguishable without colour alone, so it also
   gets a heavier border. :user-invalid rather than :invalid — the latter marks
   a required field red before the reader has typed anything. */
.inp:user-invalid,input:user-invalid,select:user-invalid,textarea:user-invalid,
.has-error .inp,.has-error input,.has-error select,.has-error textarea{
  border-color:var(--bad);border-width:1.5px;
}
::placeholder{color:var(--muted-2);opacity:1}
input:disabled,select:disabled,textarea:disabled,.inp:disabled{
  opacity:.55;cursor:not-allowed;background:var(--surface-2);
}

/* Checkbox and radio: sized for a finger, tinted army green. */
input[type=checkbox],input[type=radio]{
  width:20px;height:20px;flex:0 0 20px;margin:0;
  accent-color:var(--army);cursor:pointer;
}

/* ── File input ───────────────────────────────────────────────────────────
   The native control is an unstyleable grey box whose button text differs per
   browser. ::file-selector-button lets the *button half* be styled while the
   input keeps its native picker behaviour — no JS shim, no hidden input. */
input[type=file]{
  width:100%;max-width:100%;
  font-size:var(--fs-md);color:var(--muted);
  padding:8px;border:1px dashed var(--brd);border-radius:var(--r-md);
  background:var(--surface-2);cursor:pointer;
}
input[type=file]::file-selector-button{
  margin-right:12px;
  padding:9px 16px;min-height:var(--tap-sm);
  border:0;border-radius:var(--r-sm);cursor:pointer;
  background:linear-gradient(92deg,var(--army),var(--army-2));color:#fff;
  font-family:inherit;font-size:var(--fs-md);font-weight:700;
  transition:filter var(--dur-1) ease;
}
input[type=file]::file-selector-button:hover{filter:brightness(1.12)}
input[type=file]:focus-visible{outline:2px solid var(--focus);outline-offset:2px}

/* ════════════════════════════════════════════════════════════════════════
   BUTTONS
   ════════════════════════════════════════════════════════════════════════ */
.btn{
  background:linear-gradient(92deg,var(--army),var(--army-2));color:#fff;border:1px solid transparent;
  padding:10px 16px;min-height:44px;border-radius:10px;
  font-size:.95rem;font-weight:700;font-family:inherit;line-height:1.2;
  cursor:pointer;text-align:center;
  display:inline-flex;gap:8px;align-items:center;justify-content:center;
  box-shadow:var(--e2);
  position:relative;overflow:hidden;
  /* Stops the grey flash Android Chrome paints over a tapped button. */
  -webkit-tap-highlight-color:transparent;
  transition:filter var(--dur-1) ease,box-shadow var(--dur-1) ease,
             transform .08s ease,background var(--dur-1) ease;
}
.btn::before{content:"";position:absolute;inset:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.18),transparent);transform:translateX(-120%);transition:transform .35s ease}
.btn:hover{filter:brightness(1.08);box-shadow:var(--e3)}
.btn:hover::before{transform:translateX(120%)}
.btn:active{transform:translateY(1px);box-shadow:var(--e1)}
.btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.btn[disabled],.btn.is-disabled{
  opacity:.5;pointer-events:none;box-shadow:none;filter:none;
}
/* The primary action is the brand, not the highlight.
   In the reference design the call to action is the deep navy panel and the
   amber is a small badge on it — so `.accent` is navy with white ink (8.9:1),
   and `.gold` keeps the amber for the badge-shaped uses that were already
   asking for it. */
.btn.accent{
  background:linear-gradient(92deg,var(--army),var(--army-2));
  color:#ffffff;
}
.btn.gold{
  background:linear-gradient(92deg,var(--gold),var(--gold-2));
  /* Near-black ink on amber: white on #d8862a is 2.3:1 and unreadable. */
  color:#241a05;
}
:root[data-theme="dark"] .btn.gold{color:#1c1503}
.btn.ghost{
  background:var(--surface);border:1px solid var(--brd);color:var(--txt);
  box-shadow:var(--e1);
}
.btn.ghost:hover{border-color:var(--army-300);filter:none;background:var(--surface-2)}
.btn.danger{background:linear-gradient(92deg,var(--red),#7d1018);color:#fff}
.btn.sm{padding:8px 14px;min-height:40px;font-size:.86rem}
.btn.block{width:100%}
.btn svg{width:17px;height:17px;flex:none;stroke:currentColor;fill:none;stroke-width:2}

/* ── Progress ────────────────────────────────────────────────────────────── */
.progress{height:10px;border-radius:var(--r-pill);background:var(--surface-3);overflow:hidden;border:1px solid var(--brd-soft)}
.progress > span{display:block;height:100%;background:linear-gradient(90deg,var(--army),var(--army-400));border-radius:var(--r-pill)}
.progress.burn > span{background:linear-gradient(90deg,var(--gold-2),var(--gold))}

/* ── Badges ───────────────────────────────────────────────────────────────
   Opaque tinted grounds rather than an alpha wash: an alpha badge changes
   contrast depending on whatever it happens to sit on, which is how the light
   theme ended up with 2.9:1 status pills on a white card. */
.badge{
  padding:4px 11px;border-radius:var(--r-pill);
  font-size:var(--fs-xs);font-weight:700;letter-spacing:.3px;
  display:inline-flex;align-items:center;gap:5px;
  border:1px solid transparent;white-space:nowrap;
}
.badge.ok{background:var(--ok-bg);color:var(--ok);border-color:var(--ok-brd)}
.badge.warn{background:var(--warn-bg);color:var(--warn);border-color:var(--warn-brd)}
.badge.info{background:var(--info-bg);color:var(--army);border-color:var(--info-brd)}
.badge.gray{background:var(--surface-2);color:var(--muted);border-color:var(--brd-soft)}
.badge.army{background:var(--info-bg);color:var(--army);border-color:var(--info-brd)}
.badge.bad{background:var(--bad-bg);color:var(--bad);border-color:var(--bad-brd)}
:root[data-theme="dark"] .badge.info,
:root[data-theme="dark"] .badge.army{color:var(--gold)}

/* ════════════════════════════════════════════════════════════════════════
   MESSAGES
   ------------------------------------------------------------------------
   A dismissible strip with an icon-free, high-contrast ground. Success and
   info retire themselves after six seconds (app.js); errors stay until the
   reader closes them.
   ════════════════════════════════════════════════════════════════════════ */
.msg-stack{display:flex;flex-direction:column;gap:10px;margin-bottom:16px}
.msg{
  display:flex;align-items:flex-start;gap:12px;
  padding:12px 12px 12px 16px;border-radius:var(--r-md);
  font-size:var(--fs-md);font-weight:600;line-height:1.45;
  border:1px solid var(--brd-soft);background:var(--surface-2);color:var(--txt);
  box-shadow:var(--e1);
  animation:msgIn var(--dur-3) var(--ease-out);
}
@keyframes msgIn{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
.msg.is-going{opacity:0;transform:translateY(-6px);transition:opacity .2s ease,transform .2s ease}
.msg-text{flex:1;min-width:0}
/* A 4px rule on the leading edge carries the status, so the meaning does not
   rest on the tint alone for a reader who cannot separate the hues. */
.msg{border-left-width:4px}
.msg.success{background:var(--ok-bg);border-color:var(--ok-brd);border-left-color:var(--ok);color:var(--ok)}
.msg.error,
.msg.danger{background:var(--bad-bg);border-color:var(--bad-brd);border-left-color:var(--bad);color:var(--bad)}
.msg.warning{background:var(--warn-bg);border-color:var(--warn-brd);border-left-color:var(--warn);color:var(--warn)}
.msg.info,
.msg.debug{background:var(--info-bg);border-color:var(--info-brd);border-left-color:var(--army);color:var(--army)}
:root[data-theme="dark"] .msg.info,
:root[data-theme="dark"] .msg.debug{color:var(--gold)}
.msg-x{
  flex:none;width:28px;height:28px;border-radius:var(--r-sm);
  display:grid;place-items:center;cursor:pointer;
  background:transparent;border:0;color:inherit;opacity:.62;
  transition:opacity var(--dur-1) ease,background var(--dur-1) ease;
}
.msg-x:hover{opacity:1;background:rgba(0,0,0,.07)}
.msg-x svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2.2;stroke-linecap:round}

/* ── Toggles (access center) ─────────────────────────────────────────────── */
.switchbar{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px}
.switch{
  display:flex;align-items:center;gap:10px;padding:12px 14px;
  min-height:var(--tap);border-radius:var(--r-md);
  background:var(--surface-2);border:1px solid var(--brd);cursor:pointer;
  font-size:var(--fs-md);
  transition:border-color var(--dur-1) ease,background var(--dur-1) ease;
}
.switch:hover{border-color:var(--army-300);background:var(--surface-3)}
.switch:focus-within{border-color:var(--army);box-shadow:0 0 0 3px color-mix(in srgb,var(--army) 18%,transparent)}

/* ── Login (split-screen, responsive) ────────────────────────────────────── */
.login-wrap{display:grid;grid-template-columns:1.05fr .95fr;min-height:100vh;overflow-x:hidden}
.login-hero,.login-panel{min-width:0}
.login-hero{
  position:relative;overflow:hidden;color:#eef4f0;
  display:flex;flex-direction:column;justify-content:center;gap:22px;padding:64px 60px;
  background:
    radial-gradient(720px 420px at 18% 8%, rgba(224,164,74,.16), transparent 60%),
    radial-gradient(560px 380px at 100% 100%, rgba(18,96,63,.5), transparent 60%),
    linear-gradient(160deg,#0b2e68,#072b20 70%,#05201a);
}
.login-hero::before{
  content:"";position:absolute;inset:0;opacity:.5;pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.05) 1px,transparent 1px);
  background-size:22px 22px;
}
.login-hero > *{position:relative;z-index:1}
.login-logo{
  width:120px;height:120px;border-radius:50%;object-fit:contain;background:#fff;
  padding:12px;border:2px solid rgba(224,164,74,.55);box-shadow:0 12px 40px rgba(0,0,0,.4);
}
.login-hero h1{font-size:2.3rem;margin:0;font-weight:800;letter-spacing:-.02em;color:#fff;line-height:1.15}
/* The system's name under the mark, at the weight of a caption rather than a
   heading — the same pairing as the sidebar, so the page somebody signs in on
   and the page they land on introduce the application identically. */
.login-hero h1 small{
  display:block;margin-top:8px;font-size:.9rem;font-weight:600;
  letter-spacing:1.6px;text-transform:uppercase;color:var(--gold);
}
.login-hero .tag{color:#b9cdc2;font-size:1.02rem;max-width:440px;line-height:1.55}
.login-hero .accent-line{width:64px;height:4px;border-radius:3px;background:linear-gradient(90deg,var(--gold),transparent)}
.login-feats{list-style:none;padding:0;margin:6px 0 0;display:flex;flex-direction:column;gap:11px}
.login-feats li{display:flex;align-items:center;gap:11px;color:#cfe0d7;font-size:.94rem}
.login-feats .dot{width:26px;height:26px;flex:0 0 26px;border-radius:8px;display:flex;align-items:center;
  justify-content:center;background:rgba(224,164,74,.16);border:1px solid rgba(224,164,74,.35);color:var(--gold)}
.login-hero .hero-foot{margin-top:auto;color:#8fa89c;font-size:.78rem;letter-spacing:.3px}

.login-panel{display:flex;align-items:center;justify-content:center;padding:40px 28px;background:var(--bg-0)}
.login-card{width:100%;max-width:400px}
.login-card .mini-logo{display:none}
.login-card h2{margin:0 0 4px;font-size:1.5rem;font-weight:800;color:var(--txt-strong)}
.login-card .sub{color:var(--muted);font-size:.9rem;margin-bottom:22px}
.login-form{display:flex;flex-direction:column;gap:15px}
.login-form label{font-size:.75rem;text-transform:uppercase;letter-spacing:.6px;color:var(--muted);font-weight:700;margin-bottom:5px;display:block}
.login-form input{width:100%}

/* ── Tablet: the hero becomes a masthead above the form ──────────────────── */
@media (max-width:900px){
  .login-wrap{grid-template-columns:1fr;min-height:100dvh}
  .login-hero{
    padding:calc(30px + var(--safe-t)) 26px 26px;
    gap:12px;text-align:center;align-items:stretch;
  }
  .login-hero .login-logo,
  .login-hero .accent-line{align-self:center}
  .login-hero .tag{max-width:100%;font-size:.92rem}
  .login-feats,
  .login-hero .hero-foot{display:none}
  .login-logo{width:84px;height:84px}
  .login-hero h1{font-size:1.6rem}
  .login-hero h1 small{font-size:.72rem;letter-spacing:1.2px;margin-top:6px}
  .login-panel{padding:30px 20px calc(30px + var(--safe-b))}
  .login-card{max-width:100%}
}

/* ── Phone: the box, and nothing else ────────────────────────────────────
   The hero is a brand panel — an emblem, a strapline and four feature
   bullets. On a desktop it fills space the form does not need. On a phone it
   fills the *fold*: the two fields somebody came here to type into were below
   it, and the first thing they had to do was scroll past an advertisement for
   the site they are already on.

   So on a phone there is no hero. The emblem moves inside the card, where it
   identifies the page in one line, and the card is vertically centred like the
   sign-in sheet of any mobile application. `100dvh`, not `100vh`, or the
   browser's own URL bar counts twice and the card sits low. */
@media (max-width:767.98px){
  .login-wrap{
    display:flex;align-items:center;justify-content:center;
    min-height:100dvh;
  }
  .login-hero{display:none}
  .login-panel{
    width:100%;
    padding:calc(24px + var(--safe-t)) 16px calc(24px + var(--safe-b));
    /* Transparent, not var(--bg-0).
       The body is a soft gradient; the panel was a flat fill of the gradient's
       first stop, and it is only as tall as its content. So a phone showed a
       flat rectangle sitting on a gradient with a visible seam above and below
       it — two bands of not-quite-the-same green framing the sign-in box. */
    background:transparent;
  }
  /* An actual sheet, which is what every mobile sign-in is. Floating the
     heading and two fields directly on the page background left them with
     nothing to sit on and no edge to define the tap area. */
  .login-card{
    max-width:400px;margin-inline:auto;text-align:center;
    background:var(--surface);
    border:1px solid var(--brd-soft);
    border-radius:var(--r-xl,20px);
    box-shadow:var(--shadow-sm);
    padding:26px 20px 20px;
  }
  .login-card .mini-logo{
    display:block;width:72px;height:72px;object-fit:contain;
    margin:0 auto 16px;padding:8px;border-radius:50%;
    background:#fff;border:2px solid rgba(224,164,74,.5);
  }
  /* The fields go back to reading left, whatever the card does. */
  .login-form{text-align:left}
  .login-card h2{font-size:1.35rem}
  /* The security notice wraps to two lines at this width and was carrying the
     visual weight of body copy. It is a notice, not a paragraph. */
  .login-card .sub{font-size:.82rem;margin-bottom:18px;line-height:1.45}
  /* Fine print reads as fine print. It was the same size as the label above
     the username field, which put the copyright and the form on equal footing.

     Scoped through .login-card, and that is load-bearing rather than tidy:
     a bare `.login-footer` ties on specificity with the `.app-footer` rule far
     below in the compact-scale section, and a media query adds none — so the
     later rule won and this font-size silently did nothing. */
  .login-card .login-footer{margin-top:18px;padding-top:14px;font-size:.68rem;gap:4px}
}

/* A short landscape phone: even the emblem is more than the fold can spare. */
@media (max-height:520px) and (orientation:landscape) and (max-width:900px){
  .login-hero{display:none}
  .login-card .mini-logo{display:block;width:52px;margin:0 auto 12px}
  .login-panel{padding-block:18px}
}

/* ── Footer ──────────────────────────────────────────────────────────────── */
.app-footer{
  margin-top:44px;padding:18px 2px;border-top:1px solid var(--brd);
  display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px 18px;
  color:var(--muted);font-size:.78rem;letter-spacing:.2px;
}
.app-footer a{color:var(--gold);font-weight:700}
.app-footer a:hover{text-decoration:underline}
.login-footer{margin-top:22px;padding-top:16px;justify-content:center;text-align:center;
  flex-direction:column;gap:4px;border-top:1px solid var(--brd)}

/* ── Auth / misc ─────────────────────────────────────────────────────────── */
.center-narrow{max-width:440px;margin:8vh auto}
.stat-row{display:flex;gap:18px;flex-wrap:wrap;margin-top:8px}
.stat{flex:1;min-width:160px;padding:18px;background:var(--card-bg);border:1px solid var(--brd);border-radius:14px;box-shadow:var(--shadow-sm)}
.stat .v{font-size:1.6rem;font-weight:800;color:var(--txt-strong)}
.stat .k{color:var(--muted);font-size:.78rem;text-transform:uppercase;letter-spacing:.6px;margin-top:2px}

/* ── Modal / lightbox ────────────────────────────────────────────────────── */
.modal-bg{position:fixed;inset:0;background:rgba(3,10,7,.82);display:flex;align-items:center;justify-content:center;z-index:50;padding:24px;backdrop-filter:blur(4px)}
.modal{max-width:920px;width:100%;max-height:90vh;overflow-y:auto;padding:26px;background:var(--card-bg);border:1px solid var(--brd);border-radius:var(--radius);box-shadow:var(--shadow)}
video{width:100%;border-radius:12px;background:#000}
img{max-width:100%}

/* ── Auto-scrolling image lightbox ───────────────────────────────────────── */
.lb-bg{position:fixed;inset:0;z-index:60;display:flex;align-items:center;justify-content:center;
  padding:clamp(14px,3vw,34px);
  background:radial-gradient(120% 120% at 50% 0%,rgba(18,96,63,.28),transparent 55%),rgba(3,10,7,.92);
  backdrop-filter:blur(7px);animation:lbFade .2s ease}
@keyframes lbFade{from{opacity:0}to{opacity:1}}
.lb-close{position:absolute;top:16px;right:18px;z-index:3;width:42px;height:42px;border-radius:50%;
  border:1px solid rgba(255,255,255,.25);background:rgba(18,28,48,.5);color:#fff;font-size:1.1rem;
  cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s;backdrop-filter:blur(4px)}
.lb-close:hover{background:var(--red);border-color:transparent;transform:rotate(90deg)}
.lb-stage{display:flex;flex-direction:column;align-items:center;max-width:96vw}
.lb-frame{position:relative;display:flex;align-items:center;justify-content:center;
  border-radius:16px;overflow:hidden;
  box-shadow:0 30px 90px -24px rgba(0,0,0,.85),0 0 0 1px rgba(255,255,255,.06)}
.lb-frame img{display:block;max-width:92vw;max-height:80vh;border-radius:16px;
  background:#05100b;animation:lbZoom .35s ease}
@keyframes lbZoom{from{opacity:.4;transform:scale(.985)}to{opacity:1;transform:scale(1)}}
/* transparent prev / next controls pinned to the frame edges */
.lb-nav{position:absolute;top:50%;transform:translateY(-50%);z-index:2;
  width:clamp(40px,5vw,56px);height:clamp(40px,5vw,56px);border-radius:50%;
  border:1px solid rgba(255,255,255,.28);background:rgba(14,24,44,.32);color:#fff;
  font-size:2rem;line-height:0;cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:background .15s,transform .15s,color .15s;backdrop-filter:blur(4px)}
.lb-nav:hover{background:rgba(224,164,74,.92);color:#241a05;border-color:transparent;
  transform:translateY(-50%) scale(1.08)}
.lb-prev{left:14px}
.lb-next{right:14px}
/* overlays on the frame */
.lb-catbadge{position:absolute;top:14px;left:14px;z-index:2;padding:6px 12px;border-radius:20px;
  font-size:.74rem;font-weight:700;letter-spacing:.3px;color:#fff;
  background:linear-gradient(92deg,rgba(18,96,63,.92),rgba(11,61,46,.92));
  border:1px solid rgba(255,255,255,.18);backdrop-filter:blur(4px)}
.lb-counter{position:absolute;top:14px;right:14px;z-index:2;padding:5px 11px;border-radius:20px;
  font-size:.74rem;font-weight:700;color:#fff;background:rgba(14,24,44,.5);
  border:1px solid rgba(255,255,255,.16);backdrop-filter:blur(4px);font-variant-numeric:tabular-nums}
.lb-datetime{position:absolute;bottom:14px;right:14px;z-index:2;padding:7px 13px;border-radius:12px;
  font-size:.8rem;font-weight:700;color:#fff;letter-spacing:.3px;display:inline-flex;align-items:center;gap:6px;
  background:linear-gradient(92deg,rgba(224,164,74,.95),rgba(184,144,31,.95));color:#241a05;
  box-shadow:0 6px 18px -6px rgba(0,0,0,.6)}
.lb-datetime::before{content:"🕒";font-size:.85rem}
.lb-cap{margin-top:16px;text-align:center;color:#eef4f0;display:flex;flex-wrap:wrap;gap:8px;
  align-items:center;justify-content:center;font-size:.92rem;max-width:90vw}
.lb-cap strong{color:var(--gold);font-weight:800}
.lb-cap span{color:rgba(255,255,255,.85)}
.lb-cap em{color:rgba(255,255,255,.7);font-style:italic}
@media (max-width:560px){
  .lb-nav{width:40px;height:40px;font-size:1.6rem}
  .lb-catbadge,.lb-counter{font-size:.68rem}
}

.pill-links{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
.muted{color:var(--muted)}
.right{text-align:right}

/* ── Dashboard: KPIs / charts / activity ─────────────────────────────────── */
.kpis-5{grid-template-columns:repeat(5,1fr)}
.kpi.d{border-top:3px solid var(--ok)}
.kpi.e{border-top:3px solid var(--army)}
.kpi{transition:transform .18s ease,box-shadow .18s ease}
a.kpi:hover{transform:translateY(-4px);box-shadow:var(--shadow)}

.chart-grid{
  display:grid;grid-template-columns:repeat(2,1fr);gap:18px;margin:22px 0;
  perspective:1400px;
}
.chart-card{
  padding:18px 20px 20px;transition:transform .3s ease,box-shadow .3s ease;
  transform-style:preserve-3d;
}
.chart-card:hover{
  transform:rotateX(3deg) rotateY(-3deg) translateY(-3px);
  box-shadow:0 22px 48px rgba(0,0,0,.28);
}
.chart-title{
  font-size:.8rem;text-transform:uppercase;letter-spacing:1px;font-weight:700;
  color:var(--muted);margin-bottom:14px;display:flex;align-items:center;gap:9px;
}
.chart-title::before{content:"";width:16px;height:16px;border-radius:5px;
  background:linear-gradient(135deg,var(--gold),var(--army))}
.chart-wrap{height:260px;position:relative}

.dash-2col{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:8px}
.activity{display:flex;flex-direction:column;gap:2px;max-height:280px;overflow-y:auto}
.activity-row{display:flex;gap:12px;padding:9px 4px;border-bottom:1px solid var(--brd-soft)}
.activity-row:last-child{border-bottom:none}
.activity-icon{
  flex:0 0 34px;height:34px;border-radius:10px;display:flex;align-items:center;
  justify-content:center;font-size:.9rem;background:var(--surface-2);border:1px solid var(--brd-soft);
}
.act-created{color:var(--ok)} .act-uploaded{color:var(--gold)}
.act-deleted{color:var(--bad)} .act-progress{color:var(--army)}
.act-updated{color:var(--muted)} .act-login{color:var(--muted-2)}
.activity-body{min-width:0}
.activity-text{font-size:.86rem;line-height:1.35}
.activity-meta{font-size:.72rem;color:var(--muted);margin-top:2px}

/* ── Project detail page ─────────────────────────────────────────────────── */
.project-hero{overflow:hidden;margin-bottom:18px;padding:0}
.hero-cover{
  height:270px;background:var(--thumb-bg) center/cover no-repeat;position:relative;
}
.hero-cover::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 30%,rgba(7,20,42,.78))}
.hero-status{position:absolute;top:16px;left:16px;z-index:2;font-size:.8rem}
.hero-caption{position:absolute;left:clamp(16px,3vw,26px);right:clamp(16px,3vw,26px);
  bottom:clamp(14px,2.4vw,20px);z-index:2;color:#fff}
.hero-caption h2{margin:0;font-size:clamp(1.2rem,2.4vw,1.7rem);line-height:1.18;font-weight:800;
  letter-spacing:.2px;text-shadow:0 2px 14px rgba(0,0,0,.55)}
.hero-caption span{display:inline-block;margin-top:6px;font-size:.85rem;
  color:rgba(255,255,255,.92);text-shadow:0 1px 6px rgba(0,0,0,.55)}

.detail-grid{display:grid;grid-template-columns:1.6fr 1fr;gap:18px;margin-bottom:8px}
.detail-main{min-width:0}
.detail-side{min-width:0}

/* ── Info (left) + Video (right) split ───────────────────────────────────── */
.pv-split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.28fr);
  gap:18px;margin-bottom:18px;align-items:start}
.pv-info{padding:22px 24px;min-width:0}
.pv-info-head{display:flex;align-items:center;justify-content:space-between;
  gap:12px;margin-bottom:16px}
.pv-video{padding:18px 20px 20px;min-width:0;display:flex;flex-direction:column;
  position:relative;overflow:hidden}

/* budget KPI tiles inside the info panel */
.pv-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin:0 0 18px}
.pv-kpi{padding:12px 12px;border-radius:12px;background:var(--surface-2);
  border:1px solid var(--brd-soft);display:flex;flex-direction:column;gap:4px;
  min-width:0}
.pv-kpi .k{font-size:.66rem;text-transform:uppercase;letter-spacing:.5px;
  font-weight:700;color:var(--muted)}
.pv-kpi .v{font-size:clamp(.86rem,1.5vw,1.02rem);font-weight:800;
  font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;
  text-overflow:ellipsis}
.pv-kpi:first-child{border-top:2px solid var(--army)}
.pv-kpi:nth-child(2){border-top:2px solid var(--gold)}
.pv-kpi:last-child{border-top:2px solid var(--ok)}
/* gold accent bar across the top of the video panel */
.pv-video::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;
  background:linear-gradient(90deg,var(--army),var(--gold))}

.pv-progress{display:flex;flex-direction:column;gap:16px;margin:6px 0 18px}
.pv-prog-head{display:flex;justify-content:space-between;align-items:baseline;
  margin-bottom:7px}
.pv-prog-head span{color:var(--muted);text-transform:uppercase;letter-spacing:.5px;font-weight:700;font-size:.72rem}
.pv-prog-head strong{font-size:1.05rem;color:var(--txt);font-variant-numeric:tabular-nums}
.pv-info .progress{height:9px}

.pv-meta{display:flex;flex-direction:column;gap:0;margin:0;
  border-top:1px solid var(--brd-soft)}
.pv-meta > div{display:flex;justify-content:space-between;align-items:center;gap:14px;
  padding:11px 4px;border-bottom:1px solid var(--brd-soft);border-radius:6px;
  transition:background .15s}
.pv-meta > div:hover{background:var(--surface-2)}
.pv-meta dt{color:var(--muted);font-size:.74rem;text-transform:uppercase;
  letter-spacing:.5px;font-weight:700;margin:0}
.pv-meta dd{margin:0;font-size:.9rem;font-weight:700;text-align:right;
  font-variant-numeric:tabular-nums}
.pv-desc{margin-top:18px}

/* ── Verification video panel ────────────────────────────────────────────── */
.pv-video-head{display:flex;align-items:center;justify-content:space-between;
  gap:12px;margin-bottom:15px}
.pv-video-body{display:flex;flex-direction:column;gap:18px;flex:1}
.video-item{display:flex;flex-direction:column}
.video-frame{position:relative;border-radius:16px;overflow:hidden;
  background:#05100b;padding:6px;
  background-image:linear-gradient(#05100b,#05100b),linear-gradient(135deg,var(--army),var(--gold));
  background-origin:border-box;background-clip:content-box,border-box;
  box-shadow:0 16px 40px -18px rgba(0,0,0,.7),0 2px 8px rgba(0,0,0,.25);
  transition:transform .2s ease,box-shadow .2s ease}
.video-frame:hover{transform:translateY(-2px);
  box-shadow:0 22px 50px -18px rgba(0,0,0,.78),0 3px 10px rgba(0,0,0,.3)}
.video-frame video{display:block;width:100%;aspect-ratio:16/9;
  object-fit:cover;border-radius:11px;background:#000}
.video-bar{display:flex;align-items:center;justify-content:space-between;
  gap:10px;margin-top:12px}
.video-title{font-weight:700;font-size:.94rem;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap}
.video-actions{display:flex;gap:8px;flex-shrink:0}

.video-empty{flex:1;display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;gap:10px;padding:clamp(30px,5vw,56px) 22px;
  border:1.5px dashed var(--brd);border-radius:16px;position:relative;overflow:hidden;
  background:
    radial-gradient(130% 120% at 50% -10%,rgba(224,164,74,.10),transparent 58%),
    linear-gradient(180deg,var(--surface),var(--surface-2));
  min-height:clamp(210px,26vw,320px)}
.video-empty-ic{width:66px;height:66px;display:flex;align-items:center;justify-content:center;
  font-size:1.9rem;border-radius:18px;margin-bottom:2px;
  background:linear-gradient(135deg,rgba(18,96,63,.18),rgba(224,164,74,.18));
  border:1px solid rgba(224,164,74,.32);box-shadow:0 8px 22px -10px rgba(18,96,63,.5)}
.video-empty-title{font-weight:800;font-size:1.02rem;color:var(--txt-strong)}
.video-empty p{margin:0;max-width:300px;font-size:.84rem;line-height:1.5}
.video-empty .btn{margin-top:6px}
.video-empty-note{margin-top:4px;font-size:.76rem;font-weight:600;color:var(--muted);
  padding:7px 14px;border-radius:20px;background:var(--surface-2);border:1px solid var(--brd-soft)}

/* ── Detail charts row ───────────────────────────────────────────────────── */
.pv-charts{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:16px;margin-bottom:8px}

/* ── Media upload / edit form card ───────────────────────────────────────── */
.media-form-card{padding:24px;max-width:600px}
.media-form-head{display:flex;align-items:center;gap:14px;margin-bottom:20px;
  padding-bottom:16px;border-bottom:1px solid var(--brd-soft)}
.media-form-icon{width:46px;height:46px;flex-shrink:0;display:flex;
  align-items:center;justify-content:center;border-radius:12px;font-size:1.3rem;
  background:rgba(224,164,74,.16);border:1px solid rgba(224,164,74,.35)}
.media-form-title{font-weight:800;font-size:1.08rem}
.media-form-body{display:flex;flex-direction:column;gap:14px}

/* The photograph being corrected. Thumbnail-sized on purpose: this is an
   identity check, not a viewer, and a 4K original above the form would push
   every field off a phone screen. */
.media-edit-preview{display:flex;align-items:center;gap:14px;margin:0 0 18px}
.media-edit-preview img{
  width:132px;height:auto;aspect-ratio:22/15;object-fit:cover;
  border-radius:10px;background:var(--bg-2);border:1px solid var(--brd);
}
.media-edit-preview figcaption{font-size:.76rem;font-variant-numeric:tabular-nums}

/* The per-item actions on a photo card. A row rather than a stack, because two
   glyph buttons side by side cost one line and two stacked cost two — on a grid
   of sixty cards that is a screenful. `wrap` so a third ever added does not push
   the card wide on a phone. */
.card-actions{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
/* A card whose thumbnail and title are one link, with its own actions below —
   an <a> cannot contain another <a>, so the link wraps only the part that
   should open the card. */
.card-link{display:block;color:inherit;text-decoration:none}

/* ── Quick date filters (gallery) ────────────────────────────────────────── */
.quick-filters{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:14px}
.quick-filters a{
  padding:7px 14px;border-radius:20px;font-size:.8rem;font-weight:600;
  background:var(--surface-2);border:1px solid var(--brd);color:var(--muted);
}
.quick-filters a:hover{border-color:var(--gold);color:var(--txt)}
.quick-filters a.on{background:linear-gradient(92deg,var(--army),var(--army-2));color:#fff;border-color:transparent}

/* ════════════════════════════════════════════════════════════════════════
   TOUCH & POINTER
   ------------------------------------------------------------------------
   Keyed on `pointer:coarse` — the device's *input*, not its width. A 1024px
   tablet needs thumb-sized targets; a 900px browser window on a desktop does
   not. Width alone gets both wrong.
   ════════════════════════════════════════════════════════════════════════ */
@media (pointer:coarse){
  /* THE iOS ZOOM FIX. Safari on iOS zooms the viewport whenever a focused
     form control renders below 16px, and never zooms back out — leaving the
     reader on a page that is now wider than the screen, with a keyboard up.
     16px exactly is the threshold; this is the whole cure. */
  .inp,select,textarea,
  input[type=text],input[type=date],input[type=datetime-local],input[type=time],
  input[type=password],input[type=number],input[type=email],input[type=search],
  input[type=tel],input[type=url]{
    font-size:16px;
    min-height:var(--tap);
    padding:11px 14px;
  }
  select{padding-right:40px}
  textarea{min-height:110px}

  /* Anything tappable clears 44px. */
  .btn,.btn.sm,.icon-btn,.msg-x,.pager a,.quick-filters a,
  .lb-nav,.lb-close,.um-item,.tabbar a,.tabbar button{
    min-height:var(--tap);
  }
  .btn.sm{padding:10px 15px;font-size:var(--fs-md)}
  .quick-filters a{padding:11px 16px;display:inline-flex;align-items:center}
  .actions-cell .btn{min-width:var(--tap)}
}

/* Hover effects on a touch screen do not "hover" — they *stick*, because the
   tapped element keeps :hover until something else is tapped. `html.is-touch`
   is set by app.js from the last input actually used, so a convertible laptop
   gets the lift back the moment its mouse moves. */
.is-touch .card:hover,
.is-touch a.kpi:hover,
.is-touch .chart-card:hover,
.is-touch .video-frame:hover,
.is-touch .nav a:hover,
.is-touch .side-projects a:hover,
.is-touch .nav-sub > .nav-link:hover{
  transform:none;
}
.is-touch .btn:hover{filter:none;box-shadow:var(--e2)}

/* An active state is what replaces hover on touch: it fires on the frame the
   finger lands, so the interface feels responsive rather than laggy. */
.is-touch .card:active,
.is-touch a.kpi:active{transform:scale(.985);transition:transform .06s ease}

/* ════════════════════════════════════════════════════════════════════════
   RESPONSIVE
   ------------------------------------------------------------------------
   Four bands, each with one job. They are in ascending-specificity order so a
   narrower band always wins, and every rule below is a *layout* change — no
   component redefines its own colour or type here.

     ≤1280  dense desktop: five KPIs no longer fit across
     ≤1023  TABLET & PHONE: the sidebar becomes a drawer
      ≤767  PHONE: bottom tab bar, single-column everything
      ≤400  small phone: tighten what is left

   The old build stacked the 264px sidebar as a full-width block at the top of
   the page below 900px. With 51 project names in it, a phone user scrolled
   past the entire navigation before reaching any content — which is the
   defect this section exists to fix.
   ════════════════════════════════════════════════════════════════════════ */

@media (max-width:1280px){
  .kpis-5{grid-template-columns:repeat(3,1fr)}
}

@media (max-width:1100px){
  .pv-split{grid-template-columns:1fr}
  .pv-video{order:-1}
  .detail-grid{grid-template-columns:1fr}
}

/* ── ≤1023px — the sidebar becomes an off-canvas drawer ──────────────────── */
@media (max-width:1023.98px){
  .drawer-btn{display:inline-flex}

  /* Gated on .js: without the bundle nothing can open the drawer, so the
     sidebar must stay a visible column rather than a permanently hidden one. */
  .js .side{
    position:fixed;top:0;bottom:0;left:0;
    width:min(86vw, 320px);flex-basis:auto;
    height:100vh;height:100dvh;
    padding-left:calc(16px + var(--safe-l));
    transform:translate3d(-102%,0,0);
    transition:transform var(--dur-3) var(--ease-out);
    box-shadow:none;
    /* Not `visibility:hidden`: the drawer must stay measurable so app.js can
       read offsetWidth for the swipe threshold before it is ever opened. */
    will-change:transform;
  }
  .js.drawer-open .side{
    transform:translate3d(0,0,0);
    box-shadow:var(--e4);
  }
  .js .drawer-close{display:inline-flex}
  .js .rail-btn{display:none}

  /* The rail is a desktop affordance. A drawer is already an overlay and has
     nothing to collapse into, so a rail state remembered from a desktop
     session must not leak into the drawer as a column of unlabelled icons. */
  html[data-rail="1"] .side{width:min(86vw,320px);padding:22px 16px}
  html[data-rail="1"] .brand-text,
  html[data-rail="1"] .nav-label-text,
  html[data-rail="1"] .nav-section,
  html[data-rail="1"] .ext-mark,
  html[data-rail="1"] .nav-group > summary::after{display:revert}
  html[data-rail="1"] .nav-sub{display:block}
  html[data-rail="1"] .brand-row{flex-direction:row}
  html[data-rail="1"] .nav a,
  html[data-rail="1"] .nav-group > summary,
  html[data-rail="1"] .side-public{justify-content:flex-start;padding:12px 13px;gap:11px}
  html[data-rail="1"] .brand-logo{width:44px;height:44px;flex-basis:44px}

  /* The drawer overlays the page, so main reclaims the full width. */
  .main{padding-left:calc(var(--gutter) + var(--safe-l))}

  .kpis,.kpis-5,.chart-grid,.dash-2col{grid-template-columns:repeat(2,1fr)}
  .profile-grid{grid-template-columns:1fr}
}

/* ── ≤767px — phone: tab bar, one column, compact chrome ─────────────────── */
@media (max-width:767.98px){
  .tabbar{display:flex}

  /* Every fixed-bottom bar has to be paid for in page padding, or the last
     row of content sits underneath it and cannot be read or tapped. */
  .main{padding-bottom:calc(var(--tabbar-h) + var(--safe-b) + 12px)}

  .topbar{
    top:0;margin-bottom:16px;
    /* Flush to the top edge on a phone: a floating pill with a rounded top
       and a gap above it wastes the scarcest space on the screen. */
    margin-left:calc(var(--gutter) * -1);
    margin-right:calc(var(--gutter) * -1);
    padding-top:calc(9px + var(--safe-t));
    padding-left:calc(var(--gutter) + var(--safe-l));
    padding-right:calc(var(--gutter) + var(--safe-r));
    border-radius:0;border-left:0;border-right:0;border-top:0;
  }
  .topbar h1{font-size:1.1rem}
  .topbar h1::before{width:4px;height:17px;margin-right:9px;flex-basis:4px}

  .kpis-5,.chart-grid,.dash-2col,.detail-grid,
  .pv-kpis,.pv-charts{grid-template-columns:1fr}

  /* The three headline counters stay side by side on a phone.
     Stacked at 1fr they were three full-width cards of roughly 300px each —
     nine hundred pixels, most of it empty, to show three numbers, and the
     projects the page exists for started below the fold. A counter is a label
     and a figure; it does not need a column to itself. */
  .kpis{grid-template-columns:repeat(3,1fr);gap:8px}
  .kpi{padding:12px 10px;border-radius:var(--r-lg)}
  /* Two lines' worth of room whether the label needs them or not.
     "Ongoing projects" and "Completed projects" wrap at this width and
     "Photographs" does not, so without a floor the three figures sat at three
     different heights and the row read as broken rather than as a set. */
  .kpi .label{font-size:.6rem;letter-spacing:.4px;line-height:1.25;min-height:2.5em}
  .kpi .num{font-size:1.7rem;margin-top:4px}
  /* A 120px blurred disc behind a 100px-wide tile: all of it, and nothing
     legible under it. */
  .kpi::after{display:none}
  .grid{grid-template-columns:1fr;gap:14px}
  .stat-row{gap:12px}
  .stat{flex:1 1 100%}

  .hero-cover{height:190px}
  .pv-info{padding:18px 16px}
  .pv-video{padding:16px 14px 18px}
  .video-bar{flex-direction:column;align-items:stretch;gap:8px}
  .video-actions{justify-content:flex-end}
  .pv-video-head{flex-wrap:wrap}

  .app-footer{
    flex-direction:column;align-items:flex-start;gap:6px;
    margin-top:28px;font-size:var(--fs-xs);
  }
}

/* ── ≤400px — the smallest phones still in service ───────────────────────── */
@media (max-width:400px){
  .kpi{padding:16px 16px}
  .kpi .num{font-size:2.1rem}
  .tabbar span{font-size:.58rem}
  .topbar-tools{gap:5px}
  .card .body{padding:12px 13px}
}

/* ── Landscape phone: the tab bar would eat half the viewport ────────────── */
@media (max-height:460px) and (orientation:landscape){
  .tabbar{display:none}
  .main{padding-bottom:16px}
  .topbar{position:static}
}

/* ════════════════════════════════════════════════════════════════════════
   PUBLIC LANDING PAGE  (body.lp)  — army green + gold, light & dark aware.
   ════════════════════════════════════════════════════════════════════════ */
[x-cloak]{display:none!important}
body.lp{overflow-x:hidden}

/* Sidebar "view public site" pills — icon + label, dashed to read as external. */
.side-public{
  padding:9px 12px;border-radius:10px;font-size:.8rem;font-weight:600;
  color:#cfe0d7;border:1px dashed rgba(224,164,74,.4);transition:.15s;
  display:flex;align-items:center;gap:10px;
}
.side-public:hover{border-color:var(--gold);color:#fff;background:rgba(224,164,74,.1)}
.side-public .nav-ic{width:17px;height:17px;flex:0 0 17px}

/* ── Top nav ─────────────────────────────────────────────────────────── */
.lp-nav{
  position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:18px;
  padding:11px clamp(16px,4vw,46px);
  background:color-mix(in srgb,var(--bg-0) 84%,transparent);
  backdrop-filter:blur(14px);border-bottom:1px solid var(--brd);
}
.lp-brand{display:flex;align-items:center;gap:12px;font-weight:800;color:var(--txt-strong);white-space:nowrap}
.lp-brand img{width:42px;height:42px;object-fit:contain;filter:drop-shadow(0 2px 6px rgba(0,0,0,.3))}
.lp-brand small{display:block;font-weight:500;font-size:.68rem;color:var(--muted);letter-spacing:.2px}
.lp-nav-cta{display:flex;align-items:center;gap:10px;margin-left:auto}

/* ── Hero ────────────────────────────────────────────────────────────── */
.lp-hero{position:relative;min-height:min(92vh,820px);display:flex;align-items:center;
  overflow:hidden;isolation:isolate}
/* The slideshow is <img> now, not background-image, so it can be responsive.
   `overflow:hidden` contains the Ken Burns zoom; without it the scaled image
   paints outside the hero and widens the page on a phone. */
.lp-slides{position:absolute;inset:0;z-index:-1;overflow:hidden}
.lp-slide{position:absolute;inset:0;opacity:0;transition:opacity 1.1s ease}
.lp-slide picture{display:block;width:100%;height:100%}
.lp-slide img{width:100%;height:100%;object-fit:cover;object-position:center;display:block}
.lp-slide.on{opacity:1}

/* The darkening wash used to be the first layer of each slide's
   background-image, which meant it was re-declared inline on every slide and
   could not exist without one. As a single overlay it is declared once, covers
   the fallback panel too, and — the point — leaves the <img> free of anything
   that would stop the browser choosing its own source. */
.lp-slides::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,rgba(7,20,42,.34),rgba(7,20,42,.86));
}

/* Zoom the picture, not the slide: scaling the slide would scale the overlay
   with it and reveal an unwashed edge as the transform runs. */
.lp-slide.on img{animation:lpKen 6s ease-out both}
@keyframes lpKen{from{transform:scale(1.08)}to{transform:scale(1)}}
.lp-slide--fallback{
  opacity:1;
  background:
    radial-gradient(1000px 520px at 12% -10%, rgba(18,96,63,.55), transparent 60%),
    radial-gradient(900px 480px at 100% 0%, rgba(224,164,74,.16), transparent 55%),
    linear-gradient(165deg,#0b2e68,#072b20);
}
.lp-hero-inner{
  position:relative;z-index:2;max-width:800px;
  padding:clamp(48px,7vw,80px) clamp(18px,5vw,64px);display:flex;flex-direction:column;gap:14px;
  animation:lpRise .7s ease-out both;
}
@keyframes lpRise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
.lp-hero-logo{width:74px;height:74px;object-fit:contain;filter:drop-shadow(0 4px 14px rgba(0,0,0,.5))}
.lp-accent{width:66px;height:4px;border-radius:3px;background:linear-gradient(90deg,var(--gold),transparent)}
.lp-hero h1{font-size:clamp(2.1rem,5.4vw,3.5rem);line-height:1.06;font-weight:800;color:#fff;margin:0;
  text-shadow:0 2px 24px rgba(0,0,0,.45)}
.lp-hero .lp-sub{font-size:clamp(1rem,2vw,1.32rem);font-weight:600;color:var(--gold);margin:0}
.lp-hero .lp-tag{font-size:1.04rem;line-height:1.65;color:#e4efe9;max-width:620px;margin:2px 0 0;
  text-shadow:0 1px 10px rgba(0,0,0,.5)}
/* Every caption is in the DOM; only `.on` moves between them (landing.js).
   `position:absolute` keeps them stacked so a long caption cannot make the
   hero jump in height as the carousel advances. */
.lp-slide-caption{min-height:24px;position:relative}
.lp-cap{
  position:absolute;inset:0;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .6s ease,visibility .6s;
}
.lp-cap.on{opacity:1;visibility:visible;pointer-events:auto;position:relative}
.lp-slide-caption strong{color:#fff;font-size:1rem}
.lp-slide-caption span{color:#cfe0d7;margin-left:8px;font-size:.9rem}
.lp-slide-link{display:inline-block;margin-left:10px;color:var(--gold);font-weight:700;font-size:.85rem}
.lp-hero-cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:12px}
.lp-ghost-light{background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.35);color:#fff;backdrop-filter:blur(4px)}
.lp-ghost-light:hover{background:rgba(255,255,255,.2);border-color:var(--gold)}
.lp-arrow{position:absolute;top:50%;transform:translateY(-50%);z-index:3;width:46px;height:46px;border-radius:50%;
  background:rgba(7,20,42,.5);color:#fff;border:1px solid rgba(255,255,255,.25);font-size:1.6rem;line-height:1;
  cursor:pointer;backdrop-filter:blur(6px);transition:.15s}
.lp-arrow:hover{background:var(--army);border-color:var(--gold)}
.lp-arrow--l{left:16px}.lp-arrow--r{right:16px}
.lp-dots{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:3;display:flex;gap:9px}
.lp-dot{width:10px;height:10px;border-radius:50%;border:none;cursor:pointer;background:rgba(255,255,255,.4);transition:.2s}
.lp-dot.on{background:var(--gold);width:26px;border-radius:6px}

/* ── Generic section ─────────────────────────────────────────────────── */
.lp-section{max-width:1160px;margin:0 auto;padding:clamp(52px,7vw,92px) clamp(18px,5vw,40px)}
.lp-eyebrow{color:var(--gold);font-weight:700;font-size:.8rem;letter-spacing:1.4px;text-transform:uppercase;margin-bottom:8px}
.lp-section h2{font-size:clamp(1.55rem,3.5vw,2.3rem);font-weight:800;color:var(--txt-strong);margin:0 0 20px;line-height:1.15}
.lp-lead{font-size:1.06rem;line-height:1.8;color:var(--txt)}

/* ── About / mission ─────────────────────────────────────────────────── */
.lp-about-grid{display:grid;grid-template-columns:1.5fr 1fr;gap:40px;align-items:center}
.lp-about-copy .lp-lead{max-width:640px}
.lp-about-panel{background:var(--card-bg);border:1px solid var(--brd);border-radius:var(--radius);
  box-shadow:var(--shadow);backdrop-filter:var(--card-blur);padding:26px 26px 10px;position:relative;overflow:hidden}
.lp-about-panel::before{content:"";position:absolute;right:-40px;top:-40px;width:150px;height:150px;border-radius:50%;
  background:radial-gradient(circle,rgba(224,164,74,.16),transparent 70%)}
.lp-about-emblem{display:flex;justify-content:center;margin-bottom:12px}
.lp-about-emblem img{width:84px;height:84px;object-fit:contain;filter:drop-shadow(0 4px 12px rgba(0,0,0,.2))}
.lp-facts{list-style:none;margin:0;padding:0}
.lp-facts li{display:flex;justify-content:space-between;gap:14px;padding:13px 0;border-bottom:1px solid var(--brd-soft)}
.lp-facts li:last-child{border-bottom:none}
.lp-facts .k{color:var(--muted);font-size:.82rem;text-transform:uppercase;letter-spacing:.6px;font-weight:600}
.lp-facts .v{color:var(--txt-strong);font-weight:700;font-size:.9rem;text-align:right}

/* ── Achievement counters band ───────────────────────────────────────── */
.lp-stats-band{background:linear-gradient(150deg,var(--army-2),var(--army-3));
  border-top:1px solid var(--brd);border-bottom:1px solid var(--brd)}
.lp-stats-inner{max-width:1160px;margin:0 auto;padding:clamp(36px,5vw,56px) clamp(18px,5vw,40px)}
.lp-stats-title{text-align:center;color:var(--gold);font-weight:700;font-size:.82rem;letter-spacing:1.6px;
  text-transform:uppercase;margin-bottom:26px}
.lp-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}
.lp-stat{text-align:center;display:flex;flex-direction:column;gap:6px;padding:8px}
.lp-stat .v{font-size:clamp(2.2rem,5vw,3.2rem);font-weight:800;line-height:1;
  background:linear-gradient(100deg,#f4d778,var(--gold));-webkit-background-clip:text;background-clip:text;color:transparent}
.lp-stat .k{font-size:.78rem;text-transform:uppercase;letter-spacing:1px;color:#cfe0d7;font-weight:600}

/* ── Highlight cards (landmark projects) ─────────────────────────────── */
.lp-cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(270px,1fr));gap:22px;margin-top:28px}
.lp-card{background:var(--card-bg);border:1px solid var(--brd);border-radius:var(--radius);overflow:hidden;
  box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur);transition:.2s}
.lp-card:hover{transform:translateY(-6px);border-color:rgba(224,164,74,.5);box-shadow:var(--shadow)}
.lp-card-img{height:168px;background:var(--thumb-bg) center/cover no-repeat;display:flex;align-items:center;justify-content:center}
.lp-card-img--icon{background:linear-gradient(140deg,rgba(18,96,63,.28),rgba(224,164,74,.14))}
.lp-card-ic{font-size:2.6rem;filter:drop-shadow(0 3px 8px rgba(0,0,0,.25))}
.lp-card-body{padding:16px 18px}
.lp-card-body h3{margin:0 0 5px;font-size:1.04rem;color:var(--txt-strong);line-height:1.3}
.lp-card-body p{margin:0;color:var(--muted);font-size:.85rem}

/* ── Formations / units ──────────────────────────────────────────────── */
.lp-units-sec{background:var(--surface-2);border-top:1px solid var(--brd);border-bottom:1px solid var(--brd)}
.lp-units{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px;margin-top:28px}
.lp-unit{display:flex;align-items:center;gap:16px;padding:22px 22px;border-radius:var(--radius);
  background:var(--card-bg);border:1px solid var(--brd);box-shadow:var(--shadow-sm);transition:.2s}
.lp-unit:hover{transform:translateY(-4px);border-color:var(--gold);box-shadow:var(--shadow)}
.lp-unit-ic{width:56px;height:56px;flex:0 0 auto;border-radius:14px;display:flex;align-items:center;justify-content:center;
  font-size:1.6rem;background:linear-gradient(135deg,rgba(18,96,63,.2),rgba(224,164,74,.16));border:1px solid var(--brd)}
.lp-unit h3{margin:0 0 3px;font-size:1rem;color:var(--txt-strong)}
.lp-unit p{margin:0;color:var(--muted);font-size:.84rem}

/* ── Video ───────────────────────────────────────────────────────────── */
.lp-video-wrap{text-align:center}
.lp-video-wrap .lp-eyebrow,.lp-video-wrap h2{text-align:center}
.lp-video{max-width:940px;margin:28px auto 0;border-radius:var(--radius);overflow:hidden;
  border:1px solid var(--brd);box-shadow:var(--shadow);background:#000}
.lp-video video{display:block;width:100%;height:auto;max-height:74vh}
.lp-video-frame{position:relative;padding-top:56.25%}
.lp-video-frame iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* ── CTA band ────────────────────────────────────────────────────────── */
.lp-cta{padding:clamp(44px,6vw,76px) clamp(18px,5vw,40px);
  background:
    radial-gradient(700px 300px at 50% 0%, rgba(224,164,74,.12), transparent 70%),
    linear-gradient(150deg,var(--army),var(--army-3))}
.lp-cta-card{max-width:760px;margin:0 auto;text-align:center;color:#eaf3ee}
.lp-cta-card h2{font-size:clamp(1.5rem,3.5vw,2.2rem);font-weight:800;color:#fff;margin:0 0 12px}
.lp-cta-card p{color:#d3e3da;font-size:1.02rem;line-height:1.7;max-width:620px;margin:0 auto}
.lp-cta-row{margin-top:22px;display:flex;justify-content:center;gap:12px;flex-wrap:wrap}

/* ── Footer (brand · links · contact) ────────────────────────────────── */
.lp-footer{border-top:1px solid var(--brd);
  background:linear-gradient(180deg,var(--bg-1),var(--bg-0))}
.lp-footer-top{max-width:1140px;margin:0 auto;display:grid;
  grid-template-columns:1.6fr 1fr 1fr;gap:34px;
  padding:clamp(40px,6vw,64px) clamp(18px,5vw,40px) 34px}
.lp-footer-brand{display:flex;align-items:flex-start;gap:14px}
.lp-footer-brand img{width:52px;height:52px;object-fit:contain;flex:0 0 auto}
.lp-footer-brand strong{display:block;color:var(--txt-strong);font-size:1.05rem}
.lp-footer-brand small{color:var(--muted);font-size:.8rem}
.lp-footer-blurb{margin:10px 0 0;color:var(--muted);font-size:.86rem;line-height:1.6;max-width:340px}
.lp-footer-col h4{margin:0 0 14px;font-size:.76rem;text-transform:uppercase;letter-spacing:1.2px;
  color:var(--gold);font-weight:700}
.lp-foot-links,.lp-foot-contact{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px}
.lp-foot-links a{display:flex;align-items:center;gap:9px;color:var(--txt);font-size:.9rem;font-weight:500;transition:.15s}
.lp-foot-links a span{font-size:1.05rem}
.lp-foot-links a em{margin-left:auto;color:var(--muted-2);font-style:normal}
.lp-foot-links a:hover{color:var(--gold)}
.lp-foot-links a:hover em{color:var(--gold)}
.lp-foot-contact{color:var(--muted);font-size:.9rem}
.lp-foot-contact a{color:var(--txt)}
.lp-foot-contact a:hover{color:var(--gold)}
.lp-foot-login{display:inline-block;margin-top:4px;color:var(--gold);font-weight:700}
.lp-footer-bar{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  max-width:1140px;margin:0 auto;padding:18px clamp(18px,5vw,40px);
  border-top:1px solid var(--brd-soft);color:var(--muted);font-size:.8rem}
.lp-footer-bar a{color:var(--gold);font-weight:600}

/* ── Landing responsive ──────────────────────────────────────────────── */
@media (max-width:900px){
  .lp-about-grid{grid-template-columns:1fr;gap:26px}
}

/* ── Reduced motion ───────────────────────────────────────────────────────
   Honoured for the decoration, not the state. The drawer still opens and the
   slides still change; they simply stop sliding and zooming about it. An
   animation switched off must never take the thing it was animating with it. */
@media (prefers-reduced-motion:reduce){
  .lp-slide.on img{animation:none}
  .lp-slide{transition:none}
}

/* ── Keyboard and remote control ──────────────────────────────────────────
   A television is driven by a five-key D-pad and nothing else, so the focus
   ring is not an accessibility nicety there — it is the cursor. The default
   ring is a 1px outline that disappears against a photograph, which on a wall
   panel means nobody can tell what pressing OK will do.

   :focus-visible rather than :focus, so a mouse click does not leave a ring
   behind on a desktop. */
.lp a:focus-visible,
.lp button:focus-visible{
  outline:3px solid var(--gold,#e0a44a);
  outline-offset:3px;
  border-radius:8px;
}
.lp-dot:focus-visible,.lp-arrow:focus-visible{outline-offset:5px}

/* ── Television and large wall displays ───────────────────────────────────
   Read from three metres rather than sixty centimetres. The layout is already
   responsive by width, but width is not the variable that changes here —
   viewing distance is, and no media query can ask about that. 1600px is the
   proxy: below it are laptops and desks, above it are the projectors, 4K
   monitors and 65" panels this brigade actually hangs on walls.

   Type scales up, the reading measure stays put, and the touch-only affordances
   go away — there is no finger to swipe a television with. */
@media (min-width:1600px){
  .lp-hero{min-height:min(88vh,1000px)}
  .lp-hero h1{font-size:clamp(3.2rem,4.2vw,4.6rem)}
  .lp-hero .lp-sub{font-size:clamp(1.35rem,1.6vw,1.9rem)}
  .lp-hero .lp-tag{font-size:1.3rem;max-width:52ch}
  .lp-section h2{font-size:clamp(2rem,2.6vw,3rem)}
  .lp-stat .v{font-size:clamp(2.6rem,3.4vw,4rem)}
  /* Bigger hit areas for a remote's cursor, further from the screen edge —
     a television overscans its own borders and crops whatever sits in them. */
  .lp-arrow{width:64px;height:64px;font-size:2.2rem}
  .lp-dots{bottom:38px;gap:14px}
  .lp-dot{width:14px;height:14px}
}

@media (max-width:820px){
  .lp-stats{grid-template-columns:repeat(2,1fr);gap:26px}
  .lp-footer-top{grid-template-columns:1fr 1fr}
  .lp-footer-brand{grid-column:1/-1}
}
@media (max-width:640px){
  .lp-brand small{display:none}
  .lp-footer-top{grid-template-columns:1fr;text-align:left}
  .lp-footer-bar{flex-direction:column;text-align:center;justify-content:center}
  .lp-arrow{display:none}
}

/* ════════════════════════════════════════════════════════════════════════
   LANDING ADMIN CONTROL CENTER  (.site-admin)
   ════════════════════════════════════════════════════════════════════════ */
.site-admin-intro{color:var(--muted);max-width:760px;margin:0 0 8px}
.site-admin-intro a{color:var(--gold);font-weight:600}
.sa-form{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.sa-form .sa-wide{grid-column:1/-1}
.sa-form .sa-check{flex-direction:row;align-items:center;gap:10px}
.sa-form .sa-check input{width:18px;height:18px;accent-color:var(--army)}
.sa-help{color:var(--muted-2);font-size:.72rem;line-height:1.45}
.sa-form textarea,.sa-form input,.sa-form select{width:100%}
.sa-wide .btn{margin-top:8px}
.sa-split{display:grid;grid-template-columns:340px 1fr;gap:20px;align-items:start}
.sa-h3{margin:0 0 14px;font-size:.95rem;color:var(--txt-strong)}
.sa-stack{display:flex;flex-direction:column;gap:12px}
.sa-stack input,.sa-stack textarea,.sa-stack select{width:100%}
.sa-list{display:flex;flex-direction:column;gap:12px}
.sa-item{display:flex;align-items:center;gap:14px;padding:12px 14px;border-radius:12px;
  background:var(--card-bg);border:1px solid var(--brd);box-shadow:var(--shadow-sm)}
.sa-thumb{width:96px;height:60px;border-radius:8px;flex:0 0 auto;background:var(--thumb-bg) center/cover no-repeat}
.sa-ic{width:48px;height:48px;flex:0 0 auto;border-radius:12px;display:flex;align-items:center;justify-content:center;
  font-size:1.4rem;background:linear-gradient(135deg,rgba(18,96,63,.16),rgba(224,164,74,.14));border:1px solid var(--brd)}
.sa-item-body{flex:1;min-width:0}
.sa-item-body strong{display:block;color:var(--txt-strong);font-size:.95rem}
.sa-item-body small{color:var(--muted);font-size:.8rem;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.sa-item-meta{display:flex;gap:6px;margin-top:6px;flex-wrap:wrap}
.sa-empty{color:var(--muted);font-size:.9rem;padding:8px 2px}
.sa-item-actions{display:flex;flex-direction:column;gap:6px;flex:0 0 auto}
.sa-item-actions .btn{justify-content:center}

/* Video status panel (embed / uploaded, with remove) */
.sa-video-status{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.sa-vstat{border:1px solid var(--brd);border-radius:12px;padding:14px 16px;background:var(--surface-2);
  display:flex;flex-direction:column;gap:9px;align-items:flex-start}
.sa-vstat.on{border-color:rgba(224,164,74,.5);background:var(--surface)}
.sa-vstat-h{display:flex;align-items:center;gap:10px;justify-content:space-between;width:100%;
  font-weight:700;color:var(--txt-strong);font-size:.9rem}
.sa-vstat-url{font-size:.74rem;color:var(--muted);word-break:break-all;background:var(--input-bg);
  border:1px solid var(--brd-soft);border-radius:7px;padding:5px 8px;max-width:100%}

/* Edit page thumbnail */
.sa-editthumb{height:160px;border-radius:12px;margin-bottom:16px;
  background:var(--thumb-bg) center/cover no-repeat;border:1px solid var(--brd)}

@media (max-width:820px){
  .sa-form{grid-template-columns:1fr}
  .sa-split{grid-template-columns:1fr}
  .sa-video-status{grid-template-columns:1fr}
}

/* ============================================================================
   Completed-projects register · multi-image upload · lightbox thumbnail strip
   · project slideshow.
   ========================================================================== */


/* Crisp text everywhere: grayscale smoothing on both engines, and ligature /
   kerning enabled for the Latin face. */
body{
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}

/* ── Language toggle (topbar) ─────────────────────────────────────────────── */

/* ── Completed-projects register ──────────────────────────────────────────── */
.register-heading{
  margin:18px 0 14px;padding:14px 18px;border-radius:12px;
  background:var(--surface);border:1px solid var(--brd);
  color:var(--txt-strong);font-weight:700;font-size:1rem;text-align:center;letter-spacing:.2px;
}
.register-filters .field.grow{flex:1 1 280px}
.register-filters .count-badge{align-self:center;white-space:nowrap}

.table-wrap{
  overflow-x:auto;border-radius:var(--radius);
  border:1px solid var(--brd);background:var(--card-bg);backdrop-filter:var(--card-blur);
}
.register{width:100%;border-collapse:collapse;font-size:.88rem;min-width:820px}
.register thead th{
  position:sticky;top:0;z-index:2;
  background:var(--army-2);color:#fff;
  text-align:left;font-weight:700;font-size:.74rem;
  letter-spacing:.7px;text-transform:uppercase;
  padding:13px 14px;white-space:nowrap;
}
.register tbody td{padding:13px 14px;border-top:1px solid var(--brd-soft);vertical-align:top}
.register tbody tr:hover{background:var(--tr-hover)}
.register .col-sl{width:64px;text-align:center;font-weight:700;color:var(--gold);font-variant-numeric:tabular-nums}
.register .col-date{width:150px;white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--muted)}
.register .col-unit{width:120px}
.register .col-photos{width:90px;text-align:center}
.register .col-name a{color:var(--txt-strong);font-weight:600;text-decoration:none}
.register .col-name a:hover{color:var(--gold);text-decoration:underline}
.register .col-name .sub{display:block;margin-top:4px;color:var(--muted);font-size:.78rem}
.badge.unit{
  background:rgba(18,96,63,.18);color:var(--ok);
  border:1px solid rgba(18,96,63,.35);white-space:nowrap;
}

/* ── Multi-image upload: dropzone + previews ──────────────────────────────
   On a phone there is no "drag and drop" — the whole zone is one large tap
   target that opens the camera roll. It is deliberately tall for that reason,
   and the copy inside it says "drop or choose" rather than "drop". */
.dropzone{
  position:relative;display:grid;place-items:center;
  min-height:170px;padding:26px 18px;
  border:2px dashed var(--brd);border-radius:var(--r-lg);
  background:var(--input-bg);
  transition:border-color var(--dur-2) ease,background var(--dur-2) ease,
             transform var(--dur-2) ease;
  cursor:pointer;text-align:center;
  -webkit-tap-highlight-color:transparent;
}
.dropzone:hover,.dropzone.is-over{border-color:var(--army);background:var(--surface-2)}
.dropzone.is-over{transform:scale(1.01);border-style:solid}
.dropzone:focus-within{border-color:var(--army);box-shadow:0 0 0 3px color-mix(in srgb,var(--army) 20%,transparent)}
.dz-inner{display:flex;flex-direction:column;gap:6px;align-items:center;pointer-events:none;max-width:100%}
.dz-inner strong{color:var(--txt-strong);font-size:var(--fs-base)}
.dz-inner small{font-size:var(--fs-sm);max-width:46ch}
.dz-icon{font-size:2.1rem;line-height:1}
/* The real input covers the zone so a click or a drop lands on it. The
   ::file-selector-button styling elsewhere does not apply here — the control is
   invisible and the zone itself is the button. */
.dropzone input[type=file]{
  position:absolute;inset:0;width:100%;height:100%;
  opacity:0;cursor:pointer;padding:0;border:0;background:none;
}
.dz-summary{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  flex-wrap:wrap;
  margin-top:10px;padding:9px 14px;border-radius:var(--r-md);
  background:var(--surface);border:1px solid var(--brd-soft);
  font-size:var(--fs-sm);color:var(--txt);
}
.dz-previews{
  display:grid;gap:10px;margin-top:12px;
  grid-template-columns:repeat(auto-fill,minmax(96px,1fr));
}
.dz-thumb{
  position:relative;margin:0;border-radius:var(--r-md);overflow:hidden;
  border:1px solid var(--brd-soft);background:var(--thumb-bg);
}
.dz-thumb img{display:block;width:100%;height:72px;object-fit:cover}
.dz-thumb figcaption{
  padding:5px 7px;font-size:.64rem;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* Drop one photograph from the batch before any of it is uploaded. Revealed on
   hover, and always visible — at a thumb-sized target — where there is none. */
.dz-remove{
  position:absolute;top:5px;right:5px;
  width:26px;height:26px;border-radius:50%;
  border:1px solid rgba(255,255,255,.28);background:rgba(6,16,12,.74);
  color:#fff;font-size:.72rem;line-height:1;cursor:pointer;
  display:grid;place-items:center;
  opacity:0;transform:scale(.85);
  transition:opacity var(--dur-1) ease,transform var(--dur-1) ease,background var(--dur-1) ease;
}
.dz-thumb:hover .dz-remove,.dz-remove:focus-visible{opacity:1;transform:scale(1)}
.dz-remove:hover{background:#b3261e}
@media (pointer:coarse){
  .dz-remove{opacity:1;transform:none;width:32px;height:32px;font-size:.85rem}
  .dropzone{min-height:150px;padding:22px 14px}
  .dz-previews{grid-template-columns:repeat(auto-fill,minmax(86px,1fr))}
}
#dzNote{margin:10px 0 0}

/* ── Lightbox: play/pause + thumbnail strip ───────────────────────────────── */
.lb-playpause{
  position:absolute;bottom:14px;left:14px;z-index:2;
  width:40px;height:40px;border-radius:50%;
  border:1px solid rgba(255,255,255,.22);background:rgba(6,16,12,.72);
  color:#fff;font-size:.95rem;cursor:pointer;
  display:grid;place-items:center;
  transition:background .18s ease,transform .18s ease;
}
.lb-playpause:hover{background:var(--gold);color:#241a05;transform:scale(1.06)}
.lb-playpause:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

.lb-strip{
  display:flex;gap:8px;margin-top:14px;padding:4px;
  max-width:92vw;overflow-x:auto;scroll-behavior:smooth;
  scrollbar-width:thin;scrollbar-color:rgba(224,164,74,.5) transparent;
}
.lb-strip::-webkit-scrollbar{height:6px}
.lb-strip::-webkit-scrollbar-thumb{background:rgba(224,164,74,.5);border-radius:99px}
.lb-strip button{
  flex:none;width:76px;height:52px;padding:0;cursor:pointer;
  border-radius:8px;overflow:hidden;background:#0a1a13;
  border:2px solid transparent;opacity:.55;
  transition:opacity .18s ease,border-color .18s ease,transform .18s ease;
}
.lb-strip button img{width:100%;height:100%;object-fit:cover;display:block}
.lb-strip button:hover{opacity:.9;transform:translateY(-2px)}
.lb-strip button[aria-current="true"]{opacity:1;border-color:var(--gold)}
.lb-strip button:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

/* ── Project slideshow ────────────────────────────────────────────────────── */
.slideshow .ss-frame{
  position:relative;display:grid;place-items:center;
  background:#0a1220;border-radius:12px;overflow:hidden;
}
.slideshow .ss-frame img{width:100%;max-height:64vh;object-fit:contain;display:block}
.slideshow .ss-bar{
  display:flex;justify-content:space-between;align-items:center;gap:14px;
  flex-wrap:wrap;margin-top:14px;
}
.slideshow .ss-cap{flex:1 1 auto;text-align:center;min-width:0}
.slideshow .ss-tools{display:flex;gap:8px;align-items:center}
.slideshow .ss-strip{max-width:100%;margin-top:12px}

@media (max-width:720px){
  .register{min-width:640px}
  .register .col-date{width:110px}
  .dz-previews{grid-template-columns:repeat(auto-fill,minmax(80px,1fr))}
  .dz-remove{opacity:1;transform:none}
  .lb-strip button{width:58px;height:40px}
  .lang-seg a{padding:5px 10px;font-size:.72rem}
}

@media (prefers-reduced-motion:reduce){
  .dropzone,.lang-seg a,.lb-strip button,.lb-playpause,.dz-remove{transition:none}
  .lb-strip{scroll-behavior:auto}
}

/* ── Monitor screen control center ────────────────────────────────────────── */
.monitor-admin-form .ms-grid{
  display:grid;gap:16px 20px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));
}
.monitor-admin-form .ms-check{justify-content:center}
.ms-check-label{
  display:flex;align-items:center;gap:10px;cursor:pointer;
  font-size:.86rem;color:var(--txt);text-transform:none;letter-spacing:0;font-weight:500;
}
.ms-check-label input[type=checkbox]{width:18px;height:18px;accent-color:var(--army);flex:none}
.monitor-admin-form input[type=range]{width:100%;accent-color:var(--gold)}
.monitor-admin-form input[type=color]{
  width:100%;min-height:42px;padding:4px;cursor:pointer;
  background:var(--input-bg);border:1px solid var(--brd);border-radius:10px;
}

.ms-picker{
  display:grid;gap:10px;max-height:460px;overflow-y:auto;padding-right:6px;
  grid-template-columns:repeat(auto-fill,minmax(min(100%,360px),1fr));
  scrollbar-width:thin;scrollbar-color:var(--gold) transparent;
}
.ms-pick{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:11px 14px;border-radius:10px;
  background:var(--surface);border:1px solid var(--brd-soft);
  transition:border-color .18s ease,background .18s ease;
}
.ms-pick.on{border-color:var(--gold);background:var(--surface-2)}
.ms-pick .ms-check-label{min-width:0}
.ms-pick .ms-check-label span{
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:34ch;
}
.ms-pick-meta{display:flex;align-items:center;gap:8px;flex:none}
.ms-order{width:78px;text-align:center;padding:6px 8px !important}

@media (max-width:720px){
  .ms-pick{flex-direction:column;align-items:flex-start}
  .ms-pick .ms-check-label span{max-width:100%;white-space:normal}
}

/* ── Screen board ───────────────────────────────────────────────────────────
   One card per physical display. Read left to right: who it is, whether it is
   answering, what it is showing, and then the controls — in that order, because
   an operator arrives here already knowing which screen they mean and needing
   to know only whether it is alive. */
.screen-board{display:grid;gap:16px}

.sb-bar{
  display:flex;flex-wrap:wrap;gap:14px;align-items:center;justify-content:space-between;
  padding:14px 18px;
}
.sb-tally{display:flex;align-items:center;gap:10px;font-size:.9rem;color:var(--muted)}
.sb-tally strong{font-size:1.4rem;color:var(--txt);font-variant-numeric:tabular-nums}
.sb-bulk{display:flex;flex-wrap:wrap;gap:8px}
.sb-bulk form{display:contents}

/* The one thing on the card visible from the far side of an office: is it on. */
.sb-dot{
  width:11px;height:11px;border-radius:50%;flex:none;
  background:var(--muted-2);box-shadow:inset 0 0 0 1px rgba(0,0,0,.18);
  transition:background .3s ease,box-shadow .3s ease;
}
.sb-dot.on{background:var(--ok);box-shadow:0 0 0 4px var(--ok-bg)}

.sb-card{display:grid;gap:14px;padding:18px 20px}
.sb-card.is-offline{opacity:.72}
/* A blacked-out screen reads as blacked out on the board too, or an operator
   has to remember which of nine they darkened. */
.sb-card.is-dark{border-color:var(--warn-brd);background:var(--warn-bg)}

.sb-head{
  display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;
}
.sb-id{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-width:0}
.sb-name{font-size:1.1rem;font-weight:700;margin:0;color:var(--txt)}
.sb-where{color:var(--muted);font-size:.84rem}
.sb-state{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.sb-ago{color:var(--muted-2);font-size:.76rem;font-variant-numeric:tabular-nums}

.sb-pair{
  display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  padding:10px 14px;border-radius:10px;
  background:var(--info-bg);border:1px solid var(--info-brd);
}
.sb-pair-label{font-size:.74rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}
.sb-code{
  font-size:1.25rem;font-weight:800;letter-spacing:.22em;
  color:var(--army);font-family:inherit;
}
:root[data-theme="dark"] .sb-code{color:var(--gold)}

.sb-now{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:.88rem}
.sb-pos{color:var(--muted);font-variant-numeric:tabular-nums}

/* Each control is its own <form>; `display:contents` keeps nine of them from
   becoming nine block-level rows. */
.sb-controls{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.sb-inline{display:contents}
.sb-key{min-width:46px;font-size:1.05rem;line-height:1;padding:9px 12px}

.sb-grid{
  display:grid;gap:14px 18px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr));
}
.sb-row{display:flex;gap:8px;align-items:center}
.sb-row .inp{flex:1;min-width:0}
/* With scripting on, the select submits itself and the button beside it is
   redundant; with scripting off it is the only way to apply the choice. */
.sb-go{flex:none}

.sb-contact{margin:0}
.sb-details{border-top:1px solid var(--brd-soft);padding-top:12px}
.sb-details > summary{cursor:pointer;font-weight:600;font-size:.88rem;color:var(--muted)}
.sb-details .ms-grid{
  display:grid;gap:14px 18px;margin-top:14px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),1fr));
}
.sb-actions{display:flex;gap:10px;flex-wrap:wrap;margin-top:14px}
.sb-danger{border-top:1px dashed var(--brd-soft);padding-top:12px}

.sb-empty{padding:26px 22px;text-align:center}

/* The reply to a control press. Fixed, because the board is long and the button
   that was pressed may be far from the top of it. */
.sb-note{
  position:fixed;z-index:60;left:50%;bottom:26px;transform:translateX(-50%);
  padding:12px 20px;border-radius:12px;max-width:min(92vw,540px);
  background:var(--ok-bg);border:1px solid var(--ok-brd);color:var(--ok);
  font-size:.88rem;font-weight:600;box-shadow:0 14px 34px rgba(0,0,0,.22);
}
.sb-note.is-bad{background:var(--bad-bg);border-color:var(--bad-brd);color:var(--bad)}

@media (max-width:720px){
  .sb-row{flex-wrap:wrap}
  .sb-head{align-items:flex-start}
}

/* ── Choosing what a project puts on the wall ──────────────────────────────
   An operator doing this is usually standing at the panel itself, with a
   finger, so the whole tile is the hit target and the tiles are large enough
   to be one. `auto-fill` rather than a fixed count: the same page is read on a
   phone in a corridor and on the 65" panel it is configuring. */
.reel-grid{
  display:grid;gap:12px;
  grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
}
.reel-tile{
  position:relative;display:grid;gap:8px;cursor:pointer;
  padding:10px;border-radius:14px;
  background:var(--card);border:1px solid var(--brd);
  transition:border-color .15s ease, box-shadow .15s ease, opacity .15s ease;
}
.reel-tile:hover{border-color:var(--gold)}
.reel-tile:has(.reel-check:checked){
  border-color:var(--gold);box-shadow:0 0 0 2px rgba(224,164,74,.35)
}
/* Withdrawn or archived. Dimmed rather than hidden: the reason an operator is
   on this page is usually to find the picture they took off the wall. */
.reel-tile.is-off{opacity:.5}
.reel-tile.is-playing{border-color:var(--ok-brd)}

.reel-tile img{
  width:100%;height:auto;aspect-ratio:22/15;object-fit:cover;
  border-radius:9px;background:var(--bg-2);display:block;
}
/* Not `display:none` — a hidden input cannot be reached by a keyboard, and the
   grid has to be operable without a pointing device. */
.reel-check{
  position:absolute;top:16px;left:16px;z-index:2;
  width:22px;height:22px;accent-color:var(--gold);cursor:pointer;
}
.reel-meta{display:grid;gap:2px;min-width:0}
.reel-date{color:var(--muted-2);font-size:.74rem;font-variant-numeric:tabular-nums}
.reel-cap{
  color:var(--txt);font-size:.82rem;font-weight:600;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.reel-flags{display:flex;flex-wrap:wrap;gap:5px}

.reel-actions button[disabled]{opacity:.45;cursor:not-allowed}

.reel-videos{display:grid;gap:10px}
.reel-video{
  display:flex;align-items:center;gap:12px;cursor:pointer;
  padding:10px 12px;border-radius:12px;
  background:var(--card);border:1px solid var(--brd);
}
.reel-video.is-off{opacity:.5}
/* The width has to be stated. The poster's HTML width/height are hints only —
   a 1920px still with no CSS width lays out at 1920px and pushes the row (and
   the page) sideways on a phone. `flex:none` stops it being squeezed instead. */
.reel-video img{
  flex:none;width:104px;height:auto;aspect-ratio:16/9;object-fit:cover;
  border-radius:8px;background:var(--bg-2);
}
.reel-video input{width:20px;height:20px;accent-color:var(--gold);flex:none}
.reel-video .reel-meta{flex:1;min-width:0}

@media (max-width:520px){
  .reel-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
  .reel-video img{width:76px}
}

/* ── Sidebar: Projects expands into the project names ─────────────────────── */
.nav-group{border-radius:11px}
.nav-group > summary{
  list-style:none;cursor:pointer;
  padding:12px 14px;border-radius:12px;color:#c4d6cc;font-size:.95rem;font-weight:600;
  line-height:1.2;letter-spacing:.01em;display:flex;align-items:center;justify-content:space-between;
  gap:10px;min-height:44px;border:1px solid transparent;transition:all .16s ease;
}
.nav-group > summary::-webkit-details-marker{display:none}
.nav-group > summary::after{
  content:"›";margin-left:auto;font-size:1.1rem;line-height:1;
  transform:rotate(90deg);transition:transform .18s ease;opacity:.65;
}
.nav-group.is-open > summary::after{transform:rotate(-90deg)}
.nav-group > summary:hover{background:rgba(255,255,255,.08);color:#fff}
.nav-group > summary.active{
  background:linear-gradient(90deg, rgba(224,164,74,.24), rgba(224,164,74,.05));
  color:#fff;border-color:rgba(224,164,74,.4);box-shadow:inset 3px 0 0 var(--gold);
}
.nav-group > summary:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

.side-projects{
  /* A modest cap: the nav column scrolls, so this only stops one group from
     dominating the whole rail. */
  max-height:min(38vh,340px);overflow-y:auto;overscroll-behavior:contain;
  margin:8px 0 4px;padding:8px 8px 10px;
  border:1px solid rgba(224,164,74,.16);border-radius:12px;
  background:rgba(255,255,255,.03);box-shadow:inset 0 1px 0 rgba(255,255,255,.04);
  scrollbar-width:thin;scrollbar-color:rgba(224,164,74,.45) transparent;
}
.side-projects::-webkit-scrollbar{width:6px}
.side-projects::-webkit-scrollbar-thumb{background:rgba(224,164,74,.45);border-radius:99px}


.side-projects-head{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  margin:6px 4px 7px;padding:0 2px;
  font-size:.67rem;font-weight:700;letter-spacing:.9px;text-transform:uppercase;
  color:#8ba79a;
}
.side-projects-head:first-of-type{margin-top:2px}
.sp-all{
  padding:0 !important;font-size:.64rem;font-weight:700;color:var(--gold);text-transform:none;
  letter-spacing:0;border:0 !important;background:none !important;text-decoration:none;
}
.sp-all:hover{transform:none !important;text-decoration:underline}

.side-projects a{
  padding:9px 10px;border-radius:10px;font-size:.84rem;font-weight:600;line-height:1.35;
  gap:8px;align-items:flex-start;min-height:42px;color:#b9cdc3;
  border:1px solid transparent;background:transparent;transition:background .16s ease,border-color .16s ease,transform .16s ease;
}
.side-projects a:hover{background:rgba(255,255,255,.06);border-color:rgba(224,164,74,.22);transform:translateX(2px)}
.side-projects a.active{background:rgba(224,164,74,.14);border-color:rgba(224,164,74,.28);box-shadow:inset 2px 0 0 var(--gold)}
.sp-serial{
  flex:none;min-width:22px;text-align:center;padding:2px 0 1px;
  color:var(--gold);font-weight:800;font-size:.72rem;border-radius:999px;
  background:rgba(224,164,74,.14);
}
.sp-name{
  min-width:0;display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;
  -webkit-box-orient:vertical;overflow:hidden;word-break:break-word;
}
.sp-empty{margin:10px 6px;color:#8ba79a;font-size:.76rem;line-height:1.45}

@media (max-width:1023.98px){
  /* Inside the drawer the whole panel scrolls, so the project list can have a
     more generous cap than it gets beside a desktop's fixed-height rail. */
  .side-projects{max-height:min(46vh,420px)}
  /* Comfortable thumb targets: every drawer row clears 44px. */
  .nav-group > summary,
  .nav a,
  .side-public{min-height:var(--tap)}
  .side-projects a{min-height:var(--tap-sm);align-items:center}
  .nav-sub > .nav-link{min-height:var(--tap-sm)}
}

/* ============================================================================
   Sidebar: grouped navigation, animated expand/collapse, and an icon rail.
   ========================================================================== */

/* ── Brand row: emblem + the rail toggle ─────────────────────────────────── */
.brand-row{
  display:flex;align-items:center;gap:8px;
  padding-bottom:16px;border-bottom:1px solid rgba(224,164,74,.25);
}
.brand-row .brand{flex:1;min-width:0;padding-bottom:0;border-bottom:0}
.rail-btn{
  flex:none;width:34px;height:34px;border-radius:9px;
  display:grid;place-items:center;cursor:pointer;
  background:transparent;border:1px solid transparent;color:#9fb6aa;
  transition:background .16s ease,color .16s ease,border-color .16s ease;
}
.rail-btn:hover{background:rgba(255,255,255,.08);color:#fff;border-color:var(--brd)}
.rail-btn:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.rail-btn .nav-ic{opacity:.8}

/* ── Group body: height is driven by JS, the easing lives here ───────────── */
.nav-sub{
  overflow:hidden;
  transition:height .24s cubic-bezier(.22,.61,.36,1);
}
/* Layout only. Everything about a sub-entry's *appearance* — its size, weight,
   ink, indent and active marker — is set once, further down, in the pass that
   matches it to the section headings.

   `a.nav-link` deliberately does NOT appear in this selector. It used to, and
   an element+class selector scores (0,2,1) against the (0,2,0) of a plain
   `.nav-sub .nav-link` — so this rule beat every later one whatever the source
   order, and quietly held sub-entries at .88rem while their own section
   headings sat at .72rem. A sub-entry rendering *larger* than the section it
   belongs to is the visible half of that; the invisible half was that four
   later refinements to this menu did nothing at all.

   Keep this list at (0,2,0). Raising it again re-breaks the cascade. */
.nav-sub > .nav-link{
  display:flex;align-items:center;gap:8px;
  line-height:1.25;
  border-radius:0 var(--r-sm) var(--r-sm) 0;
}
.nav-sub .nav-external .ext-mark{margin-left:auto}

/* ── Icon rail: collapsed sidebar ────────────────────────────────────────── */
html[data-rail="1"] .side{width:74px;padding:18px 10px}
html[data-rail="1"] .brand-row{flex-direction:column;gap:10px}
html[data-rail="1"] .brand{justify-content:center}
html[data-rail="1"] .brand-logo{width:40px;height:40px;flex-basis:40px}
html[data-rail="1"] .brand-text,
html[data-rail="1"] .nav-label-text,
html[data-rail="1"] .nav-section,
html[data-rail="1"] .ext-mark,
html[data-rail="1"] .nav-group > summary::after{display:none}

/* The sub-lists are unreachable at this width; a click on a group re-expands
   the sidebar first (see sidebar.js), so hiding them here is safe. */
html[data-rail="1"] .nav-sub{display:none}

html[data-rail="1"] .nav a,
html[data-rail="1"] .nav-group > summary,
html[data-rail="1"] .side-public{justify-content:center;padding:11px 0;gap:0}
html[data-rail="1"] .nav-label{gap:0}
html[data-rail="1"] .nav a:hover,
html[data-rail="1"] .nav-group > summary:hover{transform:none}
html[data-rail="1"] .nav-ic{width:20px;height:20px;flex-basis:20px}

/* Anything that transitions the sidebar width should do it once, smoothly.
   Below the drawer breakpoint the width is fixed and `transform` is what
   animates instead — see the responsive section. */
@media (min-width:1024px){
  .side{transition:width var(--dur-2) var(--ease),padding var(--dur-2) var(--ease)}
}

@media (prefers-reduced-motion:reduce){
  .nav-sub,.side,.nav-scrim,.tabbar svg,.skip-link{transition:none}
}

/* ============================================================================
   Profile, access centre and user management.
   ========================================================================== */

.profile-grid{
  display:grid;gap:20px;align-items:start;margin-top:16px;
  grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);
}
@media (max-width:1000px){.profile-grid{grid-template-columns:1fr}}

.profile-card{padding:24px}
.pc-head{display:flex;gap:20px;align-items:flex-start;flex-wrap:wrap}
.pc-avatar{
  width:88px;height:88px;flex:0 0 88px;border-radius:22px;object-fit:cover;
  border:2px solid rgba(224,164,74,.55);box-shadow:var(--shadow-sm);
}
.pc-avatar.is-initials{
  display:grid;place-items:center;font-size:1.7rem;font-weight:800;color:#fff;
  letter-spacing:1px;background:linear-gradient(135deg,var(--army),var(--army-2));
}
.pc-id{flex:1;min-width:0;display:flex;flex-direction:column;gap:9px;align-items:flex-start}
.pc-id h2{margin:0;font-size:1.3rem;font-weight:800;color:var(--txt-strong);line-height:1.25}
.pc-facts{
  display:grid;gap:6px 22px;margin:4px 0 2px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,190px),1fr));width:100%;
}
.pc-facts > div{display:flex;gap:8px;min-width:0}
.pc-facts dt{font-size:.72rem;color:var(--muted);text-transform:uppercase;letter-spacing:.6px;flex:none}
.pc-facts dd{margin:0;font-size:.84rem;color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

.profile-form{margin-top:4px}
.pf-grid{
  display:grid;gap:16px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,220px),1fr));
  margin-bottom:18px;
}
.pf-wide{grid-column:1/-1}

/* ── Read-only permission summary ────────────────────────────────────────── */
.profile-access{padding:24px}
.perm-list{list-style:none;margin:0 0 6px;padding:0;display:flex;flex-direction:column;gap:2px}
.perm-list li{
  display:flex;align-items:center;gap:10px;padding:9px 10px;border-radius:9px;
  font-size:.84rem;color:var(--muted);
}
.perm-list li.on{color:var(--txt-strong);background:var(--surface)}
.perm-dot{
  width:9px;height:9px;border-radius:50%;flex:none;
  background:var(--brd-soft);border:1px solid var(--brd);
}
.perm-list li.on .perm-dot{background:var(--ok);border-color:var(--ok);box-shadow:0 0 0 3px rgba(67,209,138,.16)}
.perm-name{flex:1;min-width:0}
.perm-state{font-size:.7rem;letter-spacing:.3px;text-transform:uppercase;flex:none}

.scope-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
.scope-list li{font-size:.84rem}
.scope-list a{color:var(--txt);border-bottom:1px dotted var(--brd)}
.scope-list a:hover{color:var(--gold)}

/* ── Permission switches (admin) ─────────────────────────────────────────── */
.perm-grid{
  display:grid;gap:12px;margin:6px 0 8px;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));
}
.perm-switch{
  display:flex;align-items:flex-start;gap:12px;cursor:pointer;
  padding:13px 15px;border-radius:12px;
  background:var(--surface);border:1px solid var(--brd-soft);
  transition:border-color .16s ease,background .16s ease;
}
.perm-switch:hover{border-color:var(--gold)}
.perm-switch:has(input:checked){border-color:var(--gold);background:var(--surface-2)}
.perm-switch input[type=checkbox]{width:18px;height:18px;accent-color:var(--army);flex:none;margin-top:2px}
.ps-body{display:flex;flex-direction:column;gap:2px;min-width:0}
.ps-body strong{font-size:.86rem;color:var(--txt-strong);font-weight:700}
.ps-body small{font-size:.74rem;color:var(--muted);line-height:1.35}

/* ── Users table ─────────────────────────────────────────────────────────── */
.users-table{min-width:940px}
.users-table tbody tr.is-blocked{opacity:.62}
.ut-user{display:flex;align-items:center;gap:11px;min-width:0}
.ut-avatar{
  width:34px;height:34px;flex:0 0 34px;border-radius:50%;object-fit:cover;
  border:1.5px solid rgba(224,164,74,.5);
}
.ut-avatar.is-initials{
  display:grid;place-items:center;font-size:.72rem;font-weight:800;color:#fff;
  background:linear-gradient(135deg,var(--army),var(--army-2));
}
.ut-id{display:flex;flex-direction:column;min-width:0;line-height:1.25}
.ut-id strong{font-size:.86rem;color:var(--txt-strong);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.ut-id small{font-size:.72rem;color:var(--muted)}
.ut-actions{white-space:nowrap;width:190px}
.ut-actions form{display:inline-block;margin-left:6px}

/* Seven pips, one per permission — readable at a glance, no legend needed. */
.perm-pips{display:inline-flex;gap:4px}
.perm-pips i{
  width:8px;height:8px;border-radius:2px;display:block;
  background:var(--brd-soft);border:1px solid var(--brd);
}
.perm-pips i.on{background:var(--gold);border-color:var(--gold)}

.badge.army{background:rgba(18,96,63,.2);color:var(--ok);border:1px solid rgba(18,96,63,.35)}

@media (max-width:720px){
  .users-table{min-width:720px}
  .pc-head{gap:14px}
  .pc-avatar{width:64px;height:64px;flex-basis:64px;border-radius:16px}
}

/* ════════════════════════════════════════════════════════════════════════
   COMPANIES (contractors) — search, forms, documents, detail
   ════════════════════════════════════════════════════════════════════════ */

/* ── Search bar (basic + collapsible advanced) ───────────────────────────── */
.company-search{padding:16px 18px;margin-bottom:18px}
.cs-basic{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.cs-input{position:relative;flex:1 1 320px;min-width:220px}
.cs-input .cs-ic{position:absolute;left:13px;top:50%;transform:translateY(-50%);opacity:.55;font-size:.9rem;pointer-events:none}
.cs-input input{width:100%;min-width:0;padding-left:38px}
.cs-advanced{
  display:none;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));
  gap:14px;align-items:end;margin-top:16px;padding-top:16px;
  border-top:1px dashed var(--brd);
}
.cs-advanced.open{display:grid}
.cs-check{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--txt);align-self:end;padding-bottom:10px}
.cs-check input{min-width:0;width:16px;height:16px;accent-color:var(--gold)}
.cs-adv-actions{display:flex;gap:8px;align-items:end}

/* ── Companies table ─────────────────────────────────────────────────────── */
.company-table{min-width:920px}
.company-link{color:var(--army);font-weight:700}
.company-link:hover{text-decoration:underline}
.cell-sub{font-size:.74rem}
.doc-count{display:inline-flex;align-items:center;gap:4px;font-size:.78rem;color:var(--muted);
  background:var(--surface-2);border:1px solid var(--brd);border-radius:99px;padding:2px 9px}
.actions-cell{white-space:nowrap}
th.center,td.center{text-align:center}

/* ── Form: grouped cards ─────────────────────────────────────────────────── */
.form-topbar{margin-bottom:14px}
.company-form{display:flex;flex-direction:column;gap:18px;max-width:920px}
.form-card{background:var(--card-bg);border:1px solid var(--brd);border-radius:var(--radius);
  box-shadow:var(--shadow-sm);backdrop-filter:var(--card-blur);padding:22px 24px}
.fc-head{display:flex;align-items:center;gap:14px;margin-bottom:18px;
  padding-bottom:14px;border-bottom:1px solid var(--brd-soft)}
.fc-ic{width:42px;height:42px;flex:0 0 42px;display:grid;place-items:center;font-size:1.2rem;
  border-radius:12px;background:linear-gradient(140deg,rgba(224,164,74,.22),rgba(18,96,63,.18));
  border:1px solid var(--brd)}
.fc-head h3{margin:0;font-size:1.02rem;font-weight:800;color:var(--txt-strong)}
/* A form beside the record it is adding to. Stacks below the tablet
   breakpoint — the narrow pane's own floor is wider than a phone, so left
   side-by-side it takes the page with it. */
.two-pane{
  display:grid;grid-template-columns:minmax(280px,380px) 1fr;
  gap:18px;margin-top:14px;
}
.two-pane > *{min-width:0}

.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.form-grid .field.wide{grid-column:1/-1}
.form-grid .field label{display:flex;align-items:center;gap:5px}
.form-grid input,.form-grid select,.form-grid textarea{width:100%;min-width:0}
.req{color:var(--red);font-weight:700}
.fhelp{font-size:.74rem;color:var(--muted-2)}
.ferr{font-size:.76rem;color:var(--bad);font-weight:600}
.field.has-err input,.field.has-err select,.field.has-err textarea{
  border-color:var(--bad);box-shadow:0 0 0 3px rgba(255,107,107,.14)}
.form-actions{display:flex;gap:12px;justify-content:flex-end;max-width:920px}

/* ── Dropzone ────────────────────────────────────────────────────────────── */
.dropzone{
  display:flex;flex-direction:column;align-items:center;gap:6px;text-align:center;
  padding:26px 20px;border:2px dashed var(--brd);border-radius:14px;cursor:pointer;
  background:var(--surface-2);transition:border-color .16s ease,background .16s ease;
}
.dropzone:hover,.dropzone.drag{border-color:var(--gold);background:rgba(224,164,74,.08)}
.dz-ic{font-size:1.7rem;color:var(--gold)}
.dz-title{font-weight:700;color:var(--txt-strong)}
.dz-hint{font-size:.78rem;color:var(--muted)}
.dz-files{margin-top:8px;font-size:.8rem;color:var(--army);font-weight:600;word-break:break-word}

/* ── Document cards & photo grid ─────────────────────────────────────────── */
.doc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:12px}
.doc-card{display:flex;flex-direction:column;border:1px solid var(--brd);border-radius:12px;
  overflow:hidden;background:var(--surface-2);transition:transform .15s ease,box-shadow .15s ease}
.doc-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-sm);border-color:rgba(224,164,74,.45)}
.doc-thumb{height:92px;display:grid;place-items:center;background:var(--thumb-bg);overflow:hidden}
.doc-thumb img{width:100%;height:100%;object-fit:cover}
.doc-badge{font-weight:800;font-size:.9rem;letter-spacing:1px;color:#fff;padding:6px 12px;border-radius:8px;
  background:linear-gradient(135deg,var(--army),var(--army-2))}
.doc-thumb.pdf .doc-badge{background:linear-gradient(135deg,var(--red),#8f1620)}
.doc-meta{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:9px 11px}
.doc-name{font-size:.78rem;font-weight:600;color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.doc-open{font-size:.7rem;color:var(--gold);white-space:nowrap}
.doc-del{background:rgba(255,107,107,.14);border:1px solid rgba(255,107,107,.4);color:var(--bad);
  width:26px;height:26px;border-radius:8px;cursor:pointer;font-size:.8rem;flex:0 0 26px;line-height:1}
.doc-del:hover{background:var(--bad);color:#fff}
.doc-meta form{margin:0}
.doc-photo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
.doc-photo{border-radius:12px;overflow:hidden;border:1px solid var(--brd);aspect-ratio:4/3;background:var(--thumb-bg)}
.doc-photo img{width:100%;height:100%;object-fit:cover;transition:transform .25s ease}
.doc-photo:hover img{transform:scale(1.06)}

/* ── Detail banner + status card ─────────────────────────────────────────── */
.company-banner{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:14px;
  padding:20px 22px;margin-bottom:18px}
.cb-main{display:flex;align-items:center;gap:16px}
.cb-avatar{width:56px;height:56px;flex:0 0 56px;display:grid;place-items:center;border-radius:16px;
  font-size:1.5rem;font-weight:800;color:#fff;background:linear-gradient(140deg,var(--army),var(--army-2));
  border:1px solid rgba(224,164,74,.4)}
.cb-name{font-size:1.3rem;font-weight:800;color:var(--txt-strong)}
.status-form{display:flex;flex-direction:column;gap:12px}
.status-form .field{gap:5px}
.row-current td{background:var(--tr-hover)}

/* ── Rich PDF / file document cards ──────────────────────────────────────── */
.filedoc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px}
.filedoc{display:flex;gap:12px;align-items:center;padding:12px 14px;border-radius:12px;
  border:1px solid var(--brd);background:var(--surface-2);transition:border-color .15s ease,transform .15s ease}
.filedoc:hover{border-color:rgba(224,164,74,.5);transform:translateY(-2px)}
.filedoc-ic{position:relative;width:46px;height:46px;flex:0 0 46px;display:grid;place-items:center;
  font-size:1.6rem;border-radius:11px;background:var(--thumb-bg)}
.filedoc.pdf .filedoc-ic{background:linear-gradient(135deg,rgba(210,59,71,.16),rgba(143,22,32,.12))}
.filedoc-ext{position:absolute;bottom:-5px;right:-5px;font-size:.52rem;font-weight:800;letter-spacing:.5px;
  color:#fff;background:var(--army);padding:2px 5px;border-radius:5px;line-height:1}
.filedoc.pdf .filedoc-ext{background:var(--red)}
.filedoc-body{display:flex;flex-direction:column;gap:5px;min-width:0;flex:1}
.filedoc-name{font-size:.82rem;font-weight:600;color:var(--txt);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.filedoc-actions{display:flex;gap:12px}
.filedoc-actions a{font-size:.72rem;font-weight:600;color:var(--gold)}
.filedoc-actions a:hover{text-decoration:underline}

/* ── Involved-projects cards ─────────────────────────────────────────────── */
.involved-count{font-size:.7rem;font-weight:700;color:var(--gold);background:rgba(224,164,74,.18);
  border-radius:99px;padding:2px 9px;margin-left:auto;text-transform:none;letter-spacing:0}
.involved-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:14px}
.involved-card{display:flex;flex-direction:column;gap:4px;padding:16px;border-radius:14px;
  border:1px solid var(--brd);background:var(--surface-2);transition:transform .15s ease,box-shadow .15s ease,border-color .15s ease}
.involved-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-sm);border-color:rgba(224,164,74,.45)}
.involved-card.current{border-color:rgba(224,164,74,.5);
  background:linear-gradient(150deg,rgba(224,164,74,.10),var(--surface-2))}
.ic-top{display:flex;justify-content:space-between;align-items:center;gap:8px;margin-bottom:4px}
.ic-title{font-weight:700;font-size:.92rem;line-height:1.35;color:var(--txt-strong);
  display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.ic-type{font-size:.74rem}
.ic-foot{display:flex;justify-content:space-between;align-items:center;margin-top:2px}
.ic-value{font-weight:700;font-size:.82rem;color:var(--army)}
:root[data-theme="dark"] .ic-value{color:var(--gold)}

/* ── Responsive: companies table becomes stacked cards on phones ─────────── */
.empty-row{text-align:center;padding:26px}
@media (max-width:760px){
  .company-table{min-width:0}
  .company-table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
  .company-table,.company-table tbody{display:block}
  .company-table tr{display:block;border:1px solid var(--brd);border-radius:14px;
    margin-bottom:12px;padding:6px 4px;background:var(--surface-2)}
  .company-table td{display:flex;justify-content:space-between;align-items:center;gap:14px;
    border:none;padding:9px 14px;text-align:right}
  .company-table td::before{content:attr(data-label);font-weight:700;color:var(--muted);
    text-transform:uppercase;font-size:.64rem;letter-spacing:.5px;text-align:left;flex:0 0 auto}
  .company-table td.col-progress{flex-direction:column;align-items:flex-end;gap:4px}
  .company-table td.col-progress .progress{width:60%}
  .company-table tr:hover td{background:transparent}
  .company-table td.empty-row{justify-content:center}
  .company-table td.empty-row::before{content:none}
  .actions-cell{justify-content:flex-end}
}

@media (max-width:900px){
  /* Above a phone but below a desk: the two panes no longer both fit, and the
     narrow one has a 280px floor it will not go under. */
  .two-pane{grid-template-columns:1fr}
}

@media (max-width:640px){
  .form-grid{grid-template-columns:1fr}
  .form-actions{max-width:none}
  .cs-input{flex-basis:100%}
  .cs-basic .btn{flex:1 1 auto;justify-content:center}
  .company-banner{flex-direction:column;align-items:flex-start}
  .cb-avatar{width:48px;height:48px;flex-basis:48px;font-size:1.25rem}
  .stat-row .stat{flex:1 1 140px}
}

/* ── Gallery pager ────────────────────────────────────────────────────────── */
.pager{
  display:flex;align-items:center;justify-content:center;gap:14px;
  margin:22px 0 6px;
}
.pager-at{font-size:.82rem;color:var(--muted);font-variant-numeric:tabular-nums}
.btn.is-disabled{opacity:.42;pointer-events:none}

/* ══════════════════════════════════════════════════════════════════════════
   Integrated archiving & project management
   ══════════════════════════════════════════════════════════════════════════
   Built entirely from the tokens declared at the top of this file, so the
   twelve new registers inherit the existing light/dark palette, radii and
   spacing rather than introducing a second look. Nothing here sets a colour
   literally.

   Sizing is fluid rather than fixed: every heading, label and nav entry uses
   clamp(), so the interface reads at a comfortable medium size on a phone and
   grows to a desktop size without a breakpoint having to be crossed. The one
   value never allowed below 16px is a form control, because iOS Safari zooms
   the page on focus below that and never zooms back out.                     */

/* ── Fluid type ─────────────────────────────────────────────────────────── */
:root{
  --fs-nav:clamp(.86rem, .81rem + .18vw, .95rem);
  --fs-label:clamp(.71rem, .68rem + .12vw, .78rem);
  --fs-body:clamp(.88rem, .85rem + .14vw, .96rem);
  --fs-title:clamp(1.12rem, 1rem + .9vw, 1.42rem);
  --fs-huge:clamp(1.35rem, 1rem + 3.4vw, 2.1rem);
}

/* The sidebar reads a touch larger on a phone, where the drawer is the whole
   screen, and settles back down on a desktop where it is a narrow column. */
.nav a, .nav summary, .nav-link{font-size:var(--fs-nav)}
.nav-label-text{overflow:hidden;text-overflow:ellipsis}
.side-projects a{font-size:var(--fs-nav)}

/* ── Register shell: the filter bar every list page opens with ──────────── */
.register-search{padding:16px 18px;margin-bottom:18px}
.rs-basic{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.rs-input{position:relative;flex:1 1 320px;min-width:0}
.rs-input .rs-ic{
  position:absolute;left:13px;top:50%;transform:translateY(-50%);
  opacity:.55;font-size:.9rem;pointer-events:none;
}
.rs-input input{width:100%;min-width:0;padding-left:38px}
.rs-filters{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));
  gap:12px;margin-top:14px;
}
.rs-filters:empty{display:none;margin-top:0}
.rs-check{
  display:flex;align-items:center;gap:8px;font-size:var(--fs-body);
  color:var(--muted);align-self:end;padding-bottom:9px;
}
.rs-check input{width:auto;min-width:0}

/* ── A register that searches as you type ─────────────────────────────────
   The Search button is the whole of the no-JavaScript path, so it is present
   in the markup always and hidden only once the script that replaces it is
   running. Hidden from a pointer, not from the keyboard: Enter in the search
   box still submits, and a screen reader still finds it. */
.js .rs-go{
  position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;
}
.js .rs-go:focus-visible{
  position:static;width:auto;height:auto;clip:auto;overflow:visible;
}

/* Nothing to reset. Dimmed rather than removed — a control that appears and
   disappears as you type is harder to aim at than one that greys. */
.rs-reset.is-idle{opacity:.42;pointer-events:none}

.rs-status{margin:10px 0 0;font-size:.78rem;color:var(--muted-2);min-height:1em}
.rs-status:empty{display:none}

/* The old rows stay legible while the new ones are fetched. Replacing them
   with a spinner makes a fast search flash, and on a slow link it throws away
   the answer somebody is still reading. */
#registerResults{transition:opacity .12s ease}
#registerResults.is-loading{opacity:.55}
@media (prefers-reduced-motion:reduce){
  #registerResults{transition:none}
}

.register-table{width:100%;border-collapse:collapse}
.result-line{margin:0 0 12px;font-size:var(--fs-body)}

/* ── Detail pages ───────────────────────────────────────────────────────── */
.detail-head{
  display:flex;gap:18px;align-items:flex-start;justify-content:space-between;
  flex-wrap:wrap;margin-bottom:16px;
}
.dh-main{min-width:0;flex:1 1 300px}
.dh-title{margin:0 0 6px;font-size:var(--fs-title);line-height:1.25;word-break:break-word}
.dh-actions{display:flex;gap:8px;flex-wrap:wrap}

/* A label/value grid. auto-fit rather than a fixed column count, so a card
   with four facts does not leave two empty columns on a wide screen. */
.kv-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px;
}
.kv-grid > div{display:flex;flex-direction:column;gap:3px;min-width:0}
.kv-grid .k{
  font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.06em;
  color:var(--muted);font-weight:600;
}
.kv-grid .v{font-size:var(--fs-body);word-break:break-word}
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.82rem}

/* Preserve the line breaks somebody typed into a minutes or remarks box.
   pre-wrap and not pre: a long line must still wrap on a phone. */
.prewrap{white-space:pre-wrap;overflow-wrap:anywhere;margin:0}
.plain-list{margin:0;padding-left:20px}
.plain-list li{margin:4px 0;font-size:var(--fs-body)}
.record-photo{max-width:100%;height:auto;border-radius:12px;display:block}
.restricted-note{
  margin:0 0 14px;font-size:var(--fs-label);padding:9px 13px;border-radius:10px;
  background:var(--surface-2);border-left:3px solid var(--warn);
}

/* ── Timeline (postings, activities, archive movements) ─────────────────── */
.timeline{list-style:none;margin:0;padding:0}
.timeline > li{
  display:flex;gap:12px;padding:11px 0;border-bottom:1px solid var(--line);
}
.timeline > li:last-child{border-bottom:0}
.tl-icon{
  flex:0 0 28px;width:28px;height:28px;border-radius:50%;
  display:grid;place-items:center;background:var(--surface-2);font-size:.85rem;
}
.tl-body{min-width:0;flex:1;font-size:var(--fs-body)}
.tl-body p{margin:4px 0 0}
.activity-timeline > li{border-bottom:0;padding:6px 0}
.activity-timeline .tl-body{border-radius:14px}

/* ── Photo grids (events, activities) ───────────────────────────────────── */
.photo-grid{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px;
}
.photo-card{margin:0;border-radius:14px;overflow:hidden;background:var(--surface-2)}
.photo-card img{width:100%;height:150px;object-fit:cover;display:block}
.photo-card figcaption{
  padding:8px 10px;font-size:var(--fs-label);color:var(--muted);
  display:flex;flex-direction:column;gap:2px;
}

/* ── Forms ──────────────────────────────────────────────────────────────── */
.record-form{display:block}
.field.has-error input,.field.has-error select,.field.has-error textarea{
  border-color:var(--bad);
}
.field-error{color:var(--bad);font-size:var(--fs-label)}
/* An add-a-row form sitting inside a detail card, below the rows it adds to. */
.inline-form{margin-top:16px;padding-top:16px;border-top:1px solid var(--line)}
form.inline{display:inline}
.doc-upload{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}
.doc-upload button{align-self:end}

/* ── Empty states ───────────────────────────────────────────────────────── */
.empty-state .empty-lead{margin:0 0 6px;font-size:1.02rem;font-weight:600}
.empty-state p.muted{margin:0 auto 14px;max-width:46ch}

/* ── Global search ──────────────────────────────────────────────────────── */
.topsearch{position:relative;flex:1 1 220px;max-width:360px;display:flex;align-items:center}

/* ── The field ────────────────────────────────────────────────────────────
   A quiet pill that comes forward when it is being used.

   The magnifier that used to sit inside it is gone. It was decoration on a
   control that already says "Search" in its own placeholder, and it took 36px
   off the left edge — most of the room the text needed on anything narrower
   than a desktop.

   Resting state is deliberately understated: the top bar carries a heading and
   four controls, and a search box with a hard border competes with all of them
   for a reader who is not searching. Focus is where it earns attention. */
.topsearch input{
  width:100%;min-width:0;height:38px;
  padding:0 34px 0 14px;
  border-radius:var(--r-pill);
  border:1px solid transparent;
  background:color-mix(in srgb, var(--surface-2) 82%, transparent);
  font-size:.86rem;
  transition:background var(--dur-1) ease,border-color var(--dur-1) ease,
             box-shadow var(--dur-1) ease;
}
.topsearch input::placeholder{color:var(--muted-2);opacity:1;font-weight:500}
.topsearch input:hover{background:var(--surface-2)}
.topsearch input:focus-visible{
  background:var(--surface);
  border-color:color-mix(in srgb, var(--army) 38%, transparent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--army) 12%, transparent);
  outline:none;
}
/* The browser's own clear cross would sit on top of ours. */
.topsearch input::-webkit-search-cancel-button{display:none}

/* Clear button — only present once something is typed. */
.topsearch-clear{
  position:absolute;right:9px;top:50%;transform:translateY(-50%);
  width:20px;height:20px;line-height:1;border:0;border-radius:50%;
  background:var(--surface-3);color:var(--muted);
  font-size:.95rem;cursor:pointer;display:flex;align-items:center;justify-content:center;
  transition:background var(--dur-1) ease,color var(--dur-1) ease;
}
.topsearch-clear:hover{color:var(--txt-strong);background:var(--brd)}

/* ── Type-ahead panel ─────────────────────────────────────────────────────
   Anchored to the field rather than the page, so it follows the sticky topbar
   instead of drifting away from it on scroll. */
.topsearch-panel{
  position:absolute;top:calc(100% + 8px);left:0;right:0;z-index:60;
  max-height:min(60vh,420px);overflow-y:auto;overscroll-behavior:contain;
  padding:6px;border-radius:var(--r-lg);
  background:var(--surface);border:1px solid var(--brd);
  box-shadow:var(--e3);
}
.ts-row{
  display:flex;align-items:flex-start;gap:10px;
  padding:9px 10px;border-radius:var(--r-md);
  color:var(--txt);text-decoration:none;
}
/* `.on` is set by the arrow keys, :hover by the mouse — both must look the
   same or keyboard use feels like a second-class path through the list. */
.ts-row:hover,.ts-row.on{background:var(--surface-2)}
.ts-kind{
  flex:0 0 auto;margin-top:1px;padding:2px 7px;border-radius:var(--r-pill);
  background:var(--surface-3);color:var(--muted);
  font-size:.62rem;font-weight:700;text-transform:uppercase;letter-spacing:.4px;
}
.ts-text{min-width:0;display:flex;flex-direction:column;gap:1px}
.ts-title{font-size:.86rem;font-weight:600;color:var(--txt-strong);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ts-sub{font-size:.72rem;color:var(--muted);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ts-empty{margin:0;padding:14px 10px;text-align:center;color:var(--muted);font-size:.82rem}
.ts-all{
  display:block;width:100%;margin-top:4px;padding:9px 10px;
  border:0;border-top:1px solid var(--brd);border-radius:0 0 var(--r-md) var(--r-md);
  background:none;cursor:pointer;font-family:inherit;
  font-size:.78rem;font-weight:700;color:var(--army);text-align:left;
}
.ts-all:hover{background:var(--surface-2)}

/* ── Monitor control centre ───────────────────────────────────────────────
   One row: the four places an operator goes, and the state of the auto-save.
   It replaced three paragraphs that explained the page to somebody already
   reading it and hid those four links inside sentences. */
.ma-bar{
  display:flex;align-items:center;justify-content:space-between;
  gap:14px;flex-wrap:wrap;margin-bottom:18px;
}
.ma-links{display:flex;gap:8px;flex-wrap:wrap}
.ma-actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:20px}

/* The save indicator. Quiet at rest — it is telling you that nothing is
   required of you — and only coloured when something has gone wrong. */
.ma-status{
  display:inline-flex;align-items:center;gap:7px;
  padding:6px 13px;border-radius:var(--r-pill);
  font-size:.76rem;font-weight:650;white-space:nowrap;
  color:var(--muted);background:var(--surface-2);
  border:1px solid transparent;
  transition:color var(--dur-1) ease,background var(--dur-1) ease,
             border-color var(--dur-1) ease;
}
.ma-status::before{
  content:"";width:7px;height:7px;border-radius:50%;
  background:currentColor;opacity:.55;flex:0 0 7px;
}
.ma-status[data-state="pending"],
.ma-status[data-state="saving"]{color:var(--warn);background:var(--warn-bg)}
.ma-status[data-state="saved"],
.ma-status[data-state="saved-live"]{color:var(--ok);background:var(--ok-bg)}
.ma-status[data-state="error"]{
  color:var(--bad);background:var(--bad-bg,var(--surface-2));border-color:var(--bad);
}
@media (prefers-reduced-motion:reduce){ .ma-status{transition:none} }

/* A field the server refused. Marked rather than described, because the
   message is already in the status chip and repeating it beside fifty
   controls is how a page becomes unreadable. */
.monitor-admin-form .is-invalid{
  border-color:var(--bad) !important;
  box-shadow:0 0 0 3px color-mix(in srgb, var(--bad) 14%, transparent);
}

/* ── Connection banner ────────────────────────────────────────────────────
   One line, above the tab bar, over everything. Not a dialog: losing the
   uplink is a condition rather than an error, the page underneath still works
   from the cache, and a modal would demand a dismissal for something the
   reader cannot act on. */
.net-banner{
  position:fixed;left:50%;transform:translateX(-50%) translateY(140%);
  bottom:calc(var(--tabbar-h, 0px) + var(--safe-b, 0px) + 12px);
  z-index:200;max-width:min(92vw,440px);
  padding:10px 16px;border-radius:var(--r-pill);
  background:#1d2b24;color:#f3f7f4;
  font-size:.82rem;font-weight:600;text-align:center;
  box-shadow:0 12px 34px rgba(0,0,0,.34);
  opacity:0;pointer-events:none;
  transition:transform .28s cubic-bezier(.2,.9,.3,1),opacity .2s ease;
}
.net-banner.on{transform:translateX(-50%) translateY(0);opacity:1}
.net-banner.is-back{background:var(--ok,#0d7a4f)}
@media (prefers-reduced-motion:reduce){
  .net-banner{transition:opacity .01s linear}
}

/* ── The offline card ─────────────────────────────────────────────────────
   Shown for a page that was never cached. */
.offline-wrap{
  min-height:100dvh;display:flex;align-items:center;justify-content:center;
  padding:24px 18px;
}
.offline-card{
  width:min(460px,100%);text-align:center;
  background:var(--surface);border:1px solid var(--brd-soft);
  border-radius:var(--r-xl);box-shadow:var(--shadow-sm);
  padding:32px 24px 26px;
}
.offline-emblem{
  width:72px;height:72px;object-fit:contain;margin:0 auto 18px;display:block;
  padding:8px;border-radius:50%;background:#fff;
  border:2px solid rgba(224,164,74,.5);
}
.offline-card h1{margin:0 0 10px;font-size:1.3rem;font-weight:800;color:var(--txt-strong)}
.offline-card p{margin:0 0 22px;color:var(--muted);font-size:.9rem;line-height:1.6}
.offline-actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}

/* The magnifier button that opens the field. Desktop has the field itself. */
.topsearch-btn{display:none}

@media (max-width:900px){
  .topsearch-btn{display:inline-flex}

  /* ── The tools become one segmented cluster ────────────────────────────
     Four separately bordered, separately shadowed pills in a row read as four
     unrelated controls and — measured on a 411px screen — ran 20px past the
     right edge, so the avatar was cut in half. One container with borderless
     buttons inside it is both the tidier object and about thirty pixels
     narrower: three borders, three shadows and two gaps stop existing. */
  .topbar-tools{
    gap:2px;padding:3px;
    border-radius:var(--r-pill);
    background:color-mix(in srgb, var(--surface-2) 80%, transparent);
    border:1px solid var(--brd-soft);
    min-width:0;
  }
  .topbar-tools .icon-btn{
    width:38px;height:38px;min-width:38px;min-height:38px;
    border-radius:50%;background:transparent;border:0;box-shadow:none;
  }
  .topbar-tools .icon-btn:active{background:var(--surface)}
  .topbar-tools .user-menu > summary{
    background:transparent;border:0;box-shadow:none;padding:2px;
  }
  /* The notification count sat against a button border that no longer exists. */
  .topbar-tools .notif-dot{border-color:var(--surface-2)}
  /* The field was simply `display:none` here, so a phone had no search in the
     top bar at all — only the tab at the very bottom of the screen. It now
     slides over the bar when the magnifier is tapped, which is where a search
     belongs on a small screen: absent until asked for, then the whole width. */
  .topsearch{
    position:absolute;left:10px;right:10px;top:50%;transform:translateY(-50%);
    max-width:none;z-index:5;
    opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .16s ease,visibility 0s linear .16s;
  }
  .topbar.searching .topsearch{
    opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s;
  }
  /* Everything the field covers stops taking taps while it is open. */
  .topbar.searching > h1,
  .topbar.searching > .drawer-btn{opacity:0;pointer-events:none}
  .topbar.searching .topbar-tools > *:not([data-search-toggle]){display:none}
}

.facets{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.facet{
  display:inline-flex;align-items:center;gap:6px;padding:7px 12px;
  border-radius:999px;border:1px solid var(--line);
  font-size:var(--fs-body);cursor:pointer;
}
.facet.on{border-color:var(--accent);background:var(--surface-2)}
.facet input{width:auto;min-width:0;margin:0}

.search-results{display:flex;flex-direction:column;gap:10px}
.result-card{
  display:grid;gap:4px;padding:14px 16px;text-decoration:none;color:inherit;
  grid-template-columns:1fr auto;align-items:center;
}
.result-card:hover{border-color:var(--accent)}
.rc-kind{
  grid-column:1;font-size:var(--fs-label);text-transform:uppercase;
  letter-spacing:.06em;color:var(--muted);font-weight:700;
}
.rc-title{grid-column:1;font-weight:600;font-size:var(--fs-body)}
.rc-meta{
  grid-column:1;font-size:var(--fs-label);color:var(--muted);
  display:flex;gap:6px;flex-wrap:wrap;
}
.rc-ref{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  padding:1px 6px;border-radius:6px;background:var(--surface-2);
}
.result-card .badge{grid-column:2;grid-row:1/span 3;justify-self:end}

/* ── Notifications ──────────────────────────────────────────────────────── */
.notif-btn{position:relative}
.notif-dot{
  position:absolute;top:2px;right:2px;min-width:16px;height:16px;padding:0 4px;
  border-radius:999px;background:var(--bad);color:#fff;font-size:.65rem;
  display:grid;place-items:center;font-weight:700;line-height:1;
}
.notif-list{display:flex;flex-direction:column;gap:8px}
.notif-row{
  display:flex;gap:12px;padding:13px 16px;text-decoration:none;color:inherit;
  align-items:flex-start;
}
/* The unread rail moved from the anchor to the wrapper: the row now sits beside
   a "mark read" button, and the two together are the item. */
.notif-item{display:flex;align-items:stretch;gap:8px}
.notif-item .notif-row{flex:1 1 auto;min-width:0}
.notif-item.unread .notif-row{border-left:3px solid var(--accent)}
/* A dot at the end of the row, so "unread" survives the rail being clipped on a
   narrow screen and does not depend on colour alone. */
.nr-flag{
  align-self:center;flex:0 0 9px;width:9px;height:9px;border-radius:50%;
  background:var(--accent);margin-left:auto;
}
/* The button is a square against a row of variable height. */
.nr-read{display:flex}
.nr-read .btn{height:100%;padding-inline:11px}
.nr-icon{
  flex:0 0 30px;width:30px;height:30px;border-radius:50%;
  display:grid;place-items:center;background:var(--surface-2);
}
.nr-body{display:flex;flex-direction:column;gap:2px;min-width:0;font-size:var(--fs-body)}

/* ── Ranking ────────────────────────────────────────────────────────────── */
.rank-chip{
  display:inline-grid;place-items:center;min-width:26px;height:26px;padding:0 7px;
  border-radius:999px;background:var(--accent);color:#fff;
  font-weight:700;font-size:.8rem;font-variant-numeric:tabular-nums;
}
.weight-strip{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
.chip{
  padding:6px 12px;border-radius:999px;background:var(--surface-2);
  font-size:var(--fs-label);color:var(--muted);
}

/* ── Hardcopy archive: the physical address is the answer ───────────────── */
.where-card{margin-bottom:16px}
.where-label{
  margin:0 0 6px;font-size:var(--fs-label);text-transform:uppercase;
  letter-spacing:.08em;color:var(--muted);font-weight:700;
}
.where-code{
  margin:0;font-size:var(--fs-huge);font-weight:800;letter-spacing:.04em;
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere;
}
td .where{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.84rem}

/* ── Officers ───────────────────────────────────────────────────────────── */
.officer-head{display:flex;gap:14px;align-items:center;flex-wrap:wrap}
.avatar.xl{width:72px;height:72px;font-size:1.5rem;flex:0 0 72px;border-radius:50%;object-fit:cover}
.org-level{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:10px}
.org-card{display:flex;gap:12px;align-items:center;padding:13px 15px;text-decoration:none;color:inherit}
.org-card:hover{border-color:var(--accent)}
.oc-body{display:flex;flex-direction:column;gap:1px;min-width:0}
.oc-body strong{font-size:var(--fs-body)}
.oc-body .muted,.oc-body .cell-sub{font-size:var(--fs-label)}
.avatar.lg{object-fit:cover}

/* ── Pager (shared partial) ─────────────────────────────────────────────── */
.pager-count{font-size:var(--fs-body);color:var(--muted);font-variant-numeric:tabular-nums}
.btn.disabled{opacity:.42;pointer-events:none}

/* ── Phone ──────────────────────────────────────────────────────────────── */
/* The registers stop being tables here. `.table-wrap` already scrolls a wide
   table sideways, but a ten-column register is unreadable that way on a
   phone; each row becomes a card instead, and the `data-label` every cell
   already carries becomes its label. That attribute is on every table in
   every module, which is what makes one rule enough. */
@media (max-width:720px){
  .register-table thead{
    position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);
  }
  .register-table tr{
    display:block;padding:12px 4px;border-bottom:1px solid var(--line);
  }
  .register-table tr:last-child{border-bottom:0}
  .register-table td{
    display:grid;grid-template-columns:minmax(90px,38%) 1fr;gap:10px;
    padding:5px 8px;border:0;text-align:left;align-items:baseline;
  }
  .register-table td::before{
    content:attr(data-label);
    font-size:var(--fs-label);text-transform:uppercase;letter-spacing:.05em;
    color:var(--muted);font-weight:600;
  }
  .register-table td.right,.register-table td.center{text-align:left}
  .register-table td.actions-cell{
    grid-template-columns:1fr;padding-top:10px;
  }
  .register-table td.actions-cell::before{display:none}
  .register-table td.actions-cell{display:flex;gap:6px;flex-wrap:wrap}
  .register-table td.actions-cell .btn{flex:1 1 auto;justify-content:center}
  .register-table .col-progress .progress{max-width:none}
}

@media (max-width:640px){
  .rs-filters{grid-template-columns:1fr}
  .doc-upload{grid-template-columns:1fr}
  .detail-head{flex-direction:column}
  .dh-actions{width:100%}
  .dh-actions .btn{flex:1 1 auto;justify-content:center}
  .kv-grid{grid-template-columns:1fr}
  .result-card{grid-template-columns:1fr}
  .result-card .badge{grid-column:1;grid-row:auto;justify-self:start}
  .photo-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
  .photo-card img{height:118px}
  .org-level{grid-template-columns:1fr}
  .rs-basic .btn{flex:1 1 auto;justify-content:center}
  .form-actions .btn{flex:1 1 auto;justify-content:center}
  /* A thumb needs a target it can actually hit. */
  .btn.sm{min-height:36px}
  .facet{min-height:36px}
}

/* ── Dashboard panels ───────────────────────────────────────────────────── */
.dash-panels{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:12px;margin-bottom:18px;
}
.panel{padding:16px 18px}
.panel-head{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  margin:0 0 10px;font-weight:700;font-size:var(--fs-body);
}
.panel-list{list-style:none;margin:0;padding:0}
.panel-list li{
  display:flex;flex-direction:column;gap:2px;padding:7px 0;
  border-bottom:1px solid var(--line);
}
.panel-list li:last-child{border-bottom:0}
.panel-list a{font-size:var(--fs-body);text-decoration:none}
.panel-list a:hover{text-decoration:underline}
a.stat{text-decoration:none;color:inherit}
a.stat:hover{border-color:var(--accent)}

/* ── Kiosk lookup (unattended panel) ────────────────────────────────────── */
/* Sized for a person standing at arm length from a wall panel, not sitting at
   a desk: everything is a step larger than the staff interface and every
   target is comfortably thumb-sized. */
body.kiosk-lookup{
  margin:0;min-height:100dvh;display:flex;flex-direction:column;
  background:var(--page-bg);color:var(--text);
}
.kl-head{
  display:flex;gap:14px;align-items:center;padding:18px 22px;
  border-bottom:1px solid var(--line);
}
.kl-head div{display:flex;flex-direction:column}
.kl-head strong{font-size:clamp(1rem,2.4vw,1.3rem)}
.kl-head span{font-size:clamp(.8rem,1.8vw,.95rem);color:var(--muted)}
.kl-main{flex:1;padding:clamp(18px,4vw,40px);max-width:1100px;width:100%;margin:0 auto}
.kl-search{display:flex;gap:10px;flex-wrap:wrap}
.kl-search input{
  flex:1 1 320px;min-width:0;font-size:clamp(1rem,2.6vw,1.3rem);
  padding:16px 20px;border-radius:14px;border:1px solid var(--line);
  background:var(--surface);color:var(--text);
}
.kl-search button{
  padding:16px 28px;border-radius:14px;border:0;cursor:pointer;
  background:var(--accent);color:#fff;font-weight:700;
  font-size:clamp(1rem,2.4vw,1.2rem);
}
.kl-hint,.kl-count{
  margin:22px 0 0;color:var(--muted);font-size:clamp(.92rem,2vw,1.05rem);
  max-width:58ch;
}
.kl-section{
  margin:30px 0 12px;font-size:clamp(.78rem,1.7vw,.9rem);
  text-transform:uppercase;letter-spacing:.08em;color:var(--muted);
}
.kl-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:12px}
.kl-card{
  display:flex;flex-direction:column;gap:4px;padding:18px 20px;border-radius:16px;
  background:var(--surface);border:1px solid var(--line);
  text-decoration:none;color:inherit;
}
a.kl-card:hover{border-color:var(--accent)}
.kl-card strong{font-size:clamp(1rem,2.2vw,1.12rem);line-height:1.3}
.kl-card span{font-size:clamp(.82rem,1.8vw,.94rem);color:var(--muted)}
.kl-ref{
  font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.8rem;
  align-self:flex-start;padding:2px 8px;border-radius:8px;background:var(--surface-2);
}
.kl-foot{
  display:flex;gap:18px;align-items:center;flex-wrap:wrap;
  padding:16px 22px;border-top:1px solid var(--line);
  font-size:.86rem;color:var(--muted);
}
.kl-foot a{color:var(--accent);text-decoration:none;font-weight:600}
.kl-foot span{margin-left:auto}

/* ══════════════════════════════════════════════════════════════════════════
   Compact sidebar & the menu control centre
   ══════════════════════════════════════════════════════════════════════════
   The sidebar now carries ten sections instead of four. At the original
   spacing that is more than a laptop screen holds, so a reader has to scroll
   to discover that a section exists at all — which is the one thing a menu
   must never require. Everything below tightens the rhythm without dropping
   any target under the 40px a finger needs.

   Overriding the rules above rather than editing them keeps the change in one
   readable block, and keeps the original spacing one deletion away.          */

.nav a,
.nav-group > summary,
.side-public{
  padding:9px 12px;
  border-radius:10px;
  font-size:var(--fs-nav);
  font-weight:600;
  gap:10px;
  min-height:40px;
}

/* A section heading sits a step quieter than its entries are loud, so the eye
   reads the list and not the row of headings. */
.nav-group > summary{
  font-size:calc(var(--fs-nav) - .02rem);
  letter-spacing:.015em;
  opacity:.92;
}
.nav-group[open] > summary,
.nav-group > summary.active{opacity:1}

.nav{gap:1px;padding-right:6px}
.nav-sub{padding-left:6px}
.nav-sub .nav-link{
  padding:7px 12px 7px 30px;
  min-height:36px;
  font-size:calc(var(--fs-nav) - .03rem);
  font-weight:500;
  border-radius:9px;
}
/* A quiet rail down the open section, so a sub-entry reads as belonging to the
   heading above it rather than floating between two of them. */
.nav-sub{position:relative}
.nav-sub::before{
  content:"";position:absolute;left:14px;top:4px;bottom:4px;width:1px;
  background:rgba(255,255,255,.12);
}
.nav-sub .nav-link.active{font-weight:700}

.nav-ic{width:17px;height:17px;flex:0 0 17px}
.nav-ic.sm{width:15px;height:15px;flex-basis:15px}
.side-projects a{padding:6px 12px 6px 30px;min-height:34px}
.side-projects-head{padding:9px 12px 5px 14px}

/* ── Menu control centre ────────────────────────────────────────────────── */
.menu-table td{vertical-align:top}
.menu-table input[type="text"],
.menu-table select{width:100%;min-width:0}
.menu-order{width:80px;text-align:center}
.menu-label-cell{display:flex;gap:8px;align-items:center}
.menu-label-cell .nav-ic{opacity:.6;color:var(--text)}

/* A section heading row, so an editor can tell at a glance which line governs
   the whole block below it. */
.menu-row-group{background:var(--surface-2)}
.menu-row-group input[type="text"]{font-weight:700}
/* A hidden row stays legible but visibly switched off. */
.menu-row-off{opacity:.5}

.role-picker{display:flex;flex-wrap:wrap;gap:5px;max-width:420px}
.role-chip{
  display:inline-flex;align-items:center;gap:5px;padding:4px 9px;
  border-radius:999px;border:1px solid var(--line);
  font-size:.75rem;cursor:pointer;white-space:nowrap;
}
.role-chip input{width:auto;min-width:0;margin:0}
.role-chip:has(input:checked){border-color:var(--accent);background:var(--surface-2)}

@media (max-width:720px){
  .menu-table td{grid-template-columns:1fr}
  .menu-table td::before{margin-bottom:2px}
  .role-picker{max-width:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   DENSITY — the compact pass
   ══════════════════════════════════════════════════════════════════════════
   The interface was built at one size for every device, and that size was set
   by the thumb: 44px minimum on every button, link and control, everywhere.
   That is right on a phone and wasteful on a desktop, where a mouse hits a
   32px target without trying — and with twenty-two modules in the sidebar the
   waste is the difference between seeing the menu and scrolling it.

   So the rule becomes conditional rather than uniform:

     · fine pointer   (mouse, trackpad) → compact. 34–38px targets, tighter
       padding, a smaller type scale.
     · coarse pointer (finger, stylus)  → the full 44px, and slightly *more*
       breathing room than before, because that is where it earns its keep.

   `@media (pointer:coarse)` is the switch, and it is a capability query rather
   than a width query on purpose: a 13" touchscreen laptop needs thumb targets
   at a desktop width, and a 27" monitor does not need them at any width.

   Everything below either retunes a token — which cascades to every page at
   once — or tightens a component that hard-coded a value instead of reading
   one. Nothing here changes a colour.                                       */

:root{
  /* ── Type. Roughly one step down across the board. The clamp() ceilings
        come down further than the floors, so a phone changes least and a
        large screen stops shouting. ────────────────────────────────────── */
  --fs-xs:   0.7rem;
  --fs-sm:   0.765rem;
  --fs-md:   0.825rem;
  --fs-base: 0.875rem;
  --fs-lg:   clamp(0.94rem, 0.9rem + 0.18vw, 1.02rem);
  --fs-xl:   clamp(1.05rem, 0.98rem + 0.4vw, 1.22rem);
  --fs-2xl:  clamp(1.25rem, 1.1rem + 0.8vw, 1.65rem);
  --fs-3xl:  clamp(1.6rem, 1.25rem + 1.9vw, 2.3rem);

  /* ── Spacing. The gutter is what makes a page feel roomy or dense; it now
        starts tighter on a phone and tops out lower on a desktop. ──────── */
  --gutter: clamp(12px, 2.4vw, 26px);

  /* ── Radius. Large radii read as "app chrome"; smaller ones read as
        "record", which is what almost every surface here now is. ───────── */
  --r-sm: 7px;  --r-md: 9px;  --r-lg: 11px;  --r-xl: 13px;
  --radius: var(--r-xl);

  /* ── Targets. The compact defaults; `pointer:coarse` puts them back up. ─ */
  --tap: 36px;
  --tap-sm: 32px;

  /* ── Chrome. Two fewer pixels of bar is two more rows of content. ────── */
  --topbar-h: 52px;
  --tabbar-h: 54px;
  --side-w: 246px;
  --rail-w: 68px;
}

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn{
  padding:7px 13px;min-height:var(--tap);border-radius:var(--r-md);
  font-size:var(--fs-md);font-weight:650;gap:6px;
}
.btn.sm{padding:5px 10px;min-height:var(--tap-sm);font-size:var(--fs-sm);gap:5px}
.btn.lg{padding:10px 20px;min-height:42px;font-size:var(--fs-base)}
.icon-btn{min-height:var(--tap);min-width:var(--tap);border-radius:var(--r-md)}

/* ── Cards and surfaces ─────────────────────────────────────────────────── */
.glass{border-radius:var(--r-xl)}
/* Every panel tightens together at this width. 22px of gutter either side of a
   320px screen is 44px — a seventh of the display — spent on nothing, on a page
   that is usually six stacked cards. This one rule is the whole reason the
   padding was moved out of 112 inline styles and into a class: an inline style
   is unreachable from here without `!important`. */
.pad{padding:15px}
.pad-sm{padding:13px}
.pad-flush{padding:4px 4px 6px}
.section-title{
  margin:20px 0 10px;font-size:var(--fs-xs);letter-spacing:1.1px;gap:8px;
}
.section-title::before{width:16px}

.stat{
  min-width:132px;padding:12px 14px;border-radius:var(--r-lg);
}
.stat .v{font-size:1.22rem;font-weight:800;line-height:1.15}
.stat .k{font-size:var(--fs-xs);letter-spacing:.5px;margin-top:1px}
.stat-row{gap:10px}

.kpis{gap:12px;margin-bottom:16px}
.kpi .num{font-size:clamp(1.5rem,1.2rem+1.2vw,2rem)}
.kpi .label{font-size:var(--fs-xs)}

/* ── Tables ─────────────────────────────────────────────────────────────── */
/* A register is a dense object by nature. These rows were sized for reading a
   paragraph; they are reading a field. */
table th{
  padding:9px 11px;font-size:var(--fs-xs);letter-spacing:.5px;
}
table td{padding:8px 11px;font-size:var(--fs-md)}
.register-table td,.company-table td{line-height:1.35}
.cell-sub{font-size:var(--fs-xs);opacity:.72}
.badge{padding:2px 8px;font-size:var(--fs-xs);border-radius:var(--r-pill)}
.progress{height:6px;border-radius:var(--r-pill)}

/* ── Forms ──────────────────────────────────────────────────────────────── */
.inp,input[type="text"],input[type="search"],input[type="number"],
input[type="date"],input[type="time"],input[type="email"],input[type="url"],
input[type="password"],select,textarea{
  padding:7px 11px;min-height:var(--tap);border-radius:var(--r-md);
  font-size:var(--fs-md);
}
textarea{min-height:auto;line-height:1.45}
.field > label,.field > span:first-child{
  font-size:var(--fs-xs);letter-spacing:.35px;margin-bottom:3px;
}
.field small{font-size:var(--fs-xs);line-height:1.35}
.form-grid{gap:12px}
.form-actions{gap:8px;margin-top:14px}

/* ── Shell ──────────────────────────────────────────────────────────────── */
.topbar h1{font-size:var(--fs-lg);font-weight:700;letter-spacing:-.01em}
.main-content{padding-top:12px}
.brand-text{font-size:var(--fs-md)}
.brand-text small{font-size:.58rem}
.brand-logo{width:38px;height:38px;flex-basis:38px}
.user-name{font-size:var(--fs-sm)}
.user-role{font-size:var(--fs-xs)}
.avatar{width:32px;height:32px;flex-basis:32px;font-size:var(--fs-sm)}
.avatar.lg{width:42px;height:42px;flex-basis:42px;font-size:var(--fs-base)}
.app-footer{font-size:var(--fs-xs);padding-block:12px}
.msg{padding:9px 13px;font-size:var(--fs-md);border-radius:var(--r-md)}

/* ── The archiving surfaces ─────────────────────────────────────────────── */
.register-search{padding:12px 14px;margin-bottom:12px}
.rs-basic{gap:8px}
.rs-filters{gap:10px;margin-top:10px}
.dh-title{font-size:var(--fs-xl)}
.detail-head{padding:16px 18px !important;margin-bottom:12px}
.kv-grid{gap:12px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.kv-grid .v{font-size:var(--fs-md)}
.panel{padding:13px 15px}
.panel-head{font-size:var(--fs-md);margin-bottom:7px}
.panel-list li{padding:5px 0}
.panel-list a{font-size:var(--fs-md)}
.dash-panels{gap:10px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.timeline > li{padding:8px 0}
.tl-icon{flex-basis:24px;width:24px;height:24px;font-size:.78rem}
.result-card{padding:11px 13px}
.notif-row{padding:11px 13px}
  /* On a phone the row takes the width and the button tucks under it,
     which keeps both at a thumb-sized target. */
  .notif-item{flex-wrap:wrap}
  .nr-read{width:100%}
  .nr-read .btn{width:100%;justify-content:center;min-height:36px}
.nr-icon{flex-basis:26px;width:26px;height:26px}
.photo-card img{height:132px}
.empty-state{padding:26px !important}
.pager{margin:16px 0 4px}

/* ── Pointer-dependent sizing ───────────────────────────────────────────── */
/* Everything above assumes a mouse. Below is the same interface for a finger:
   full 44px targets, and a little more padding than the original had, because
   a compact layout with thumb-sized targets is the combination that actually
   works on a phone. `font-size:16px` on the controls is not a preference — it
   is the exact threshold below which iOS Safari zooms on focus and never
   zooms back out. */
@media (pointer:coarse){
  :root{--tap:44px;--tap-sm:40px}

  .btn{padding:10px 16px;font-size:var(--fs-base)}
  .btn.sm{padding:8px 13px;font-size:var(--fs-md)}

  .inp,input[type="text"],input[type="search"],input[type="number"],
  input[type="date"],input[type="time"],input[type="email"],input[type="url"],
  input[type="password"],select,textarea{
    font-size:16px;padding:10px 13px;
  }

  .nav a,.nav-group > summary,.side-public{min-height:44px;padding:10px 13px}
  .nav-sub .nav-link{min-height:40px;padding:9px 13px 9px 30px}
  .role-chip,.facet{min-height:34px}
  table td{padding:10px 12px}
}

/* ── Phone ──────────────────────────────────────────────────────────────── */
@media (max-width:640px){
  /* A card per record, not a table — the rule that turns every register into
     something readable on a phone is in the archiving block above; this just
     tightens what it produces. */
  .register-table tr{padding:10px 2px}
  .register-table td{padding:4px 6px;gap:8px}
  .stat{min-width:calc(50% - 5px);padding:11px 12px}
  .stat .v{font-size:1.1rem}
  .section-title{margin:16px 0 8px}
  .detail-head{padding:14px 15px !important}
  .glass{border-radius:var(--r-lg)}
  .panel{padding:12px 13px}
  .main-content{padding-top:8px}
}

/* ══════════════════════════════════════════════════════════════════════════
   MENU & BUTTONS — the fine pass
   ══════════════════════════════════════════════════════════════════════════
   Shrinking a control is not the same as making it look right at that size.
   Four things that read as "polished" at 44px read as "muddy" at 32px, and
   all four are removed here rather than merely scaled down:

     · **Elevation shadows.** A two-layer drop shadow under a small button
       blurs its own edge — the shadow is a larger share of the shape. Small
       buttons get a hairline border and a single tight shadow instead.
     · **The sheen sweep.** `.btn::before` slides a highlight across on hover.
       On a wide call-to-action that reads as quality; on a 30px icon button it
       is a flicker. Kept for large buttons, off below that.
     · **Weight 700.** At 0.8rem heavy type fills its own counters and looks
       soft. 600 with a hair of letter-spacing is crisper at the same size.
     · **Three active-state effects at once.** The menu's current row marks
       itself with a gradient *and* a border *and* an inset rail. One strong
       signal reads faster than three competing ones, so it keeps the rail and
       a flat tint.

   The hover nudge (`translateX(3px)`) also goes: in a list of four rows it is
   playful, in a list of forty-four it is jitter.

   As everywhere in the density work, `pointer:coarse` puts the comfortable
   sizes back for a finger.                                                  */

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn{
  padding:6px 12px;
  min-height:32px;
  border-radius:var(--r-sm);
  font-size:var(--fs-sm);
  font-weight:600;
  letter-spacing:.012em;
  gap:6px;
  /* A hairline and one tight shadow, instead of a two-layer elevation that
     would be wider than the button is tall. */
  box-shadow:0 1px 1px rgba(0,0,0,.10);
  border-color:rgba(0,0,0,.06);
}
.btn:hover{filter:brightness(1.06);box-shadow:0 2px 5px rgba(0,0,0,.14)}
.btn:active{transform:translateY(.5px);box-shadow:0 1px 1px rgba(0,0,0,.10)}

/* The sweep is a large-button flourish. Below that it is a flicker. */
.btn::before{display:none}
.btn.lg::before{display:block}

.btn svg{width:14px;height:14px;stroke-width:2.1}

.btn.sm{
  padding:4px 9px;min-height:27px;font-size:var(--fs-xs);
  gap:4px;border-radius:calc(var(--r-sm) - 1px);letter-spacing:.02em;
}
.btn.sm svg{width:12px;height:12px}

.btn.lg{
  padding:10px 20px;min-height:40px;font-size:var(--fs-base);
  border-radius:var(--r-md);box-shadow:var(--e2);
}
.btn.lg svg{width:17px;height:17px}

/* Ghost is an outline button. An elevation shadow under an outline reads as a
   mistake — it looks lifted and unfilled at the same time. */
.btn.ghost{box-shadow:none}
.btn.ghost:hover{box-shadow:0 1px 3px rgba(0,0,0,.09)}

/* A square icon button, so it does not sit taller than the text buttons
   beside it in the same row. */
.icon-btn{min-height:32px;min-width:32px;width:32px;height:32px;border-radius:var(--r-sm)}
.icon-btn svg{width:16px;height:16px}

/* Actions in a table cell are the smallest buttons in the application and the
   most numerous. They sit tight and wrap cleanly. */
.actions-cell{gap:4px}
.actions-cell .btn{padding:3px 8px;min-height:25px;font-size:var(--fs-xs)}

/* ── Sidebar ────────────────────────────────────────────────────────────── */
.nav{margin-top:12px;gap:1px}

.nav a,
.nav-group > summary,
.side-public{
  padding:6px 10px;
  min-height:31px;
  border-radius:var(--r-sm);
  font-size:var(--fs-sm);
  font-weight:550;
  letter-spacing:.008em;
  gap:9px;
  border:0;
  /* Only the ground and the ink move. No transform: a nudge on hover is
     motion in the reader's peripheral vision on every one of forty-four rows. */
  transition:background var(--dur-1) ease,color var(--dur-1) ease;
}
.nav a:hover,
.nav-group > summary:hover,
.side-public:hover{
  background:rgba(255,255,255,.07);color:#fff;transform:none;
}

/* One signal, not three: a flat tint and the gold rail. */
.nav a.active,
.nav-group > summary.active{
  background:rgba(224,164,74,.15);
  color:#fff;
  border:0;
  box-shadow:inset 2px 0 0 var(--gold);
}

.nav-ic{width:15px;height:15px;flex-basis:15px;stroke-width:1.8;opacity:.62}
.nav-label{gap:9px}

/* A section heading is quieter than the entries under it, so the eye reads
   the list rather than the row of headings. */
.nav-group > summary{font-weight:600;opacity:.86}
.nav-group[open] > summary,
.nav-group > summary.active{opacity:1}

/* Sub-entries: smaller, lighter, clearly subordinate, and indented to the
   hairline rail rather than to an arbitrary number. */
.nav-sub{padding-left:5px}
.nav-sub::before{left:12px;top:2px;bottom:2px}
.nav-sub .nav-link{
  padding:5px 10px 5px 25px;
  min-height:28px;
  font-size:var(--fs-xs);
  font-weight:500;
  border-radius:calc(var(--r-sm) - 1px);
  letter-spacing:.01em;
}
.nav-sub .nav-link.active{font-weight:700;background:rgba(224,164,74,.13)}

.side-projects a{padding:5px 10px 5px 25px;min-height:27px;font-size:var(--fs-xs)}
.side-projects-head{padding:8px 10px 4px 12px;font-size:.6rem;letter-spacing:1.2px}
.sp-serial{font-size:.62rem}
.nav-section{margin:11px 11px 3px;font-size:.58rem;letter-spacing:1.3px}
.nav-external .ext-mark{font-size:.72rem}

/* The brand block sets the sidebar's top weight; at this density it was the
   heaviest thing in the column. */
.brand-row{padding-block:10px}
.brand-logo{width:34px;height:34px;flex-basis:34px}
.brand-text{font-size:var(--fs-sm);font-weight:700}
/* At the tightest density the tagline is the first thing to go: the mark
   identifies the app, the line under it only describes it. */
.brand-text small{display:none}

/* ── Touch ──────────────────────────────────────────────────────────────── */
/* Everything above is sized for a cursor. A finger gets the room back — and a
   menu is where that matters most, because a mis-tap there navigates
   somewhere the reader did not ask to go. */
@media (pointer:coarse){
  .btn{padding:9px 15px;min-height:44px;font-size:var(--fs-base);border-radius:var(--r-md)}
  .btn svg{width:16px;height:16px}
  .btn.sm{padding:7px 12px;min-height:38px;font-size:var(--fs-md)}
  .btn.sm svg{width:14px;height:14px}
  .icon-btn{min-height:42px;min-width:42px;width:42px;height:42px}
  .actions-cell .btn{padding:7px 12px;min-height:38px;font-size:var(--fs-md)}

  .nav a,.nav-group > summary,.side-public{
    min-height:44px;padding:10px 13px;font-size:var(--fs-md);
  }
  .nav-sub .nav-link{min-height:40px;padding:9px 13px 9px 30px;font-size:var(--fs-sm)}
  .side-projects a{min-height:38px;padding:8px 13px 8px 30px;font-size:var(--fs-sm)}
  .nav-ic{width:17px;height:17px;flex-basis:17px}
}

/* A collapsed rail shows icons only, so the icon becomes the whole target and
   is centred rather than left-aligned against a label that is not there. */
@media (min-width:1025px){
  html[data-rail="1"] .nav a,
  html[data-rail="1"] .nav-group > summary,
  html[data-rail="1"] .side-public{justify-content:center;padding:8px}
  html[data-rail="1"] .nav-ic{width:18px;height:18px;flex-basis:18px;opacity:.8}
}

/* ── Form finish ─────────────────────────────────────────────────────────────
   The density block above sets the *size* of a control. This sets how it
   behaves under a hand, which is what separates a form that feels considered
   from one that merely fits.

   Three ideas, and they are the same three on every control on every page:

   1. A field at rest is a recess, not a raised box. One hairline inset shadow
      does it. Every other surface in the system casts a shadow outward, so a
      field that catches light the other way reads as somewhere to put
      something without needing a border heavy enough to shout.

   2. Focus lifts. The recess inverts to a soft outward shadow, the ring comes
      up, and the border takes the army green. Moving between two states rather
      than merely adding a ring is what makes tabbing through a long form feel
      like something is following you.

   3. Nothing moves position. No translate, no scale, no growing padding — a
      field that shifts when it is focused pushes the label off the line the eye
      was resting on, and in a twenty-field form that is seasickness. */

.inp,select,textarea,
input[type=text],input[type=date],input[type=datetime-local],input[type=time],
input[type=password],input[type=number],input[type=email],input[type=search],
input[type=tel],input[type=url]{
  box-shadow:inset 0 1px 2px rgb(15 23 42 / .05);
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             background var(--dur-2) var(--ease);
}
.inp:hover,select:hover,textarea:hover,
input:hover:not([type=checkbox]):not([type=radio]):not([type=file]){
  box-shadow:inset 0 1px 2px rgb(15 23 42 / .06);
}
/* The recess inverts. Both shadows are declared so the transition has two ends
   to move between rather than snapping from inset to outset. */
.inp:focus,select:focus,textarea:focus,
input:focus:not([type=checkbox]):not([type=radio]){
  box-shadow:0 0 0 3px color-mix(in srgb,var(--army) 18%,transparent),
             0 2px 8px -2px rgb(15 23 42 / .18);
}
:root[data-theme="dark"] .inp:focus,
:root[data-theme="dark"] select:focus,
:root[data-theme="dark"] textarea:focus,
:root[data-theme="dark"] input:focus:not([type=checkbox]):not([type=radio]){
  box-shadow:0 0 0 3px color-mix(in srgb,var(--gold) 18%,transparent),
             0 2px 8px -2px rgb(0 0 0 / .4);
}

/* Placeholders sit a step further back than help text. They are a hint about
   the field, not content in it, and at the same weight as a real value they
   are read as one — which is how a form ends up submitted with "e.g. Lobby
   wall" in it. */
::placeholder{color:var(--muted-2);opacity:.78}

/* The label. Small caps was already right; it was just heavy. 650 and less
   tracking says "field name" without competing with the value underneath. */
.field label,.form-label,.field > span:first-child{
  font-weight:650;letter-spacing:.32px;color:var(--muted);
}
/* Help under a field is the quietest text on the page and should look it. */
.fhelp,.field small{color:var(--muted-2);line-height:1.4}

/* A smaller, softer chevron. The old one was sized for a control two pixels
   taller than the one it now sits in. */
select{background-size:15px;background-position:right 11px center;padding-right:34px}

/* ── The box a form sits in ──────────────────────────────────────────────── */
/* Two stacked shadows for the same reason the master-data cards have two: a
   tight contact shadow reads as touching the page, a wide soft one reads as
   height, and one blurred shadow gives neither. */
.form-card{
  padding:20px 22px;
  box-shadow:0 1px 2px rgb(15 23 42 / .05), 0 10px 28px -20px rgb(15 23 42 / .3);
}
.fc-head{margin-bottom:16px;padding-bottom:12px}
.fc-head h3{font-size:var(--fs-lg);font-weight:750;letter-spacing:-.01em}
.fc-ic{width:36px;height:36px;flex-basis:36px;font-size:1rem;border-radius:10px}

/* Actions sit on their own shelf, divided from the fields. Without the rule
   the primary button floats in whitespace and reads as unrelated to the form
   it submits. */
.form-actions{
  padding-top:14px;margin-top:16px;
  border-top:1px solid var(--brd-soft);
  align-items:center;flex-wrap:wrap;
}

/* ── File input and dropzone ─────────────────────────────────────────────── */
input[type=file]{
  transition:border-color var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
input[type=file]:hover{border-color:var(--army-300);background:var(--surface-3)}
input[type=file]::file-selector-button{
  padding:7px 14px;min-height:32px;font-size:var(--fs-sm);
  box-shadow:0 1px 2px rgb(15 23 42 / .18);
}

/* ── Errors ──────────────────────────────────────────────────────────────── */
/* A field in error keeps the lifted shape so it does not also change depth
   while it changes colour — two signals for one fault reads as two faults. */
.field.has-err input,.field.has-err select,.field.has-err textarea,
.has-error .inp,.has-error input,.has-error select,.has-error textarea{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--bad) 16%,transparent);
}
.ferr,.field-error{font-weight:650;line-height:1.4}

@media (prefers-reduced-motion:reduce){
  .inp,select,textarea,input[type=file],
  input[type=text],input[type=date],input[type=datetime-local],input[type=time],
  input[type=password],input[type=number],input[type=email],input[type=search],
  input[type=tel],input[type=url]{transition:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   SUBMENU CLARITY & CONTENT FLOW — the last pass
   ══════════════════════════════════════════════════════════════════════════
   Two things left, and the first is a trap: "smaller" and "clearer" pull in
   opposite directions past a point. A sub-entry at 0.7rem in a 500 weight on a
   dark ground is *small*, and it is also the first thing to go soft — thin
   strokes on a dark background bloom, which is why light-on-dark type needs
   slightly more weight than dark-on-light to read the same.

   So the sub-entries get smaller **and** crisper by moving three dials that
   are not size:

     · **weight up, not down** (500 → 560). On a dark ground that reads sharper
       at a smaller size, not heavier.
     · **ink up** (#c4d6cc → a brighter resting colour). The old value was
       tuned for 0.95rem rows; at 0.72rem it needs the contrast back.
     · **tracking out** (+0.02em). Small type needs air between letters far
       more than it needs air around the line.

   The active sub-entry also stops relying on weight alone: it gets a dot on
   the hairline rail, which is a position signal rather than a shape one and is
   readable at any size.

   The second half is the page rather than the menu: at 1620px a paragraph runs
   to two hundred characters, which nobody reads. Prose gets a measure; data
   does not, because a table wants every pixel it can have.               */

/* ── Sub-entries ────────────────────────────────────────────────────────── */
.nav-sub .nav-link{
  font-size:.72rem;
  font-weight:560;
  letter-spacing:.022em;
  color:#b9cfc4;
  padding:5px 10px 5px 26px;
  min-height:27px;
  /* Only the ground moves; see the note on hover nudges in the pass above. */
  transition:background var(--dur-1) ease,color var(--dur-1) ease;
}
.nav-sub .nav-link:hover{background:rgba(255,255,255,.075);color:#fff}

/* The rail the sub-entries hang from. Thinner and dimmer than before: it is a
   grouping cue, not a border, and at this density it was competing with the
   entries themselves. */
.nav-sub::before{
  left:11px;width:1px;top:3px;bottom:3px;
  background:rgba(255,255,255,.10);
}

/* Active: a dot on the rail. Position reads faster than weight, and unlike a
   background tint it stays legible when several rows are hovered in sequence. */
.nav-sub .nav-link{position:relative}
.nav-sub .nav-link.active{
  color:#fff;font-weight:700;background:rgba(224,164,74,.13);
}
.nav-sub .nav-link.active::before{
  content:"";position:absolute;left:9px;top:50%;
  width:5px;height:5px;margin-top:-2.5px;border-radius:50%;
  background:var(--gold);
}

/* The project names under the Projects section are the same kind of entry and
   should read the same way. */
.side-projects a{
  font-size:.72rem;font-weight:540;letter-spacing:.018em;
  color:#b9cfc4;padding:5px 10px 5px 26px;min-height:26px;
}
.side-projects a:hover{color:#fff}
.side-projects a.active{color:#fff;font-weight:700}
.sp-serial{
  font-size:.6rem;font-variant-numeric:tabular-nums;opacity:.62;
  min-width:16px;
}
.sp-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.side-projects-head{
  font-size:.575rem;font-weight:800;letter-spacing:1.35px;
  padding:9px 10px 3px 11px;opacity:.72;
}
.sp-empty{font-size:.68rem;padding:2px 10px 6px 26px;opacity:.6}

/* ── Content flow at full page width ────────────────────────────────────── */
/* A line of prose past roughly 80 characters is measurably harder to read: the
   eye loses the return sweep. Data is exempt — a register wants every pixel —
   so this caps prose only, and does it with `max-width` on the text rather
   than on its container, so the card still fills the page. */
.prewrap,
.empty-state p,
.restricted-note,
.sa-help,
.site-admin-intro,
.panel + p.muted,
.glass > p.muted{max-width:78ch}

/* Long unbroken values — a checksum, a URL, a chassis number — must wrap
   rather than push a card wide enough to scroll the page sideways. */
.kv-grid .v,
.rc-meta,
.tl-body,
.panel-list li,
.result-card{overflow-wrap:anywhere}

/* Body copy tracks the fluid scale, so a 4K panel and a phone both read at a
   comfortable size without a breakpoint for each. */
body{font-size:var(--fs-base);line-height:1.5}
p{line-height:1.55}
.muted{font-size:var(--fs-md)}

/* On a very wide screen the shell stops growing and centres, so the reader is
   not tracking across a metre of desk to follow a row. */
@media (min-width:1750px){
  .main{max-width:1500px;margin-inline:auto}
}

/* ── Touch ──────────────────────────────────────────────────────────────── */
@media (pointer:coarse){
  .nav-sub .nav-link{
    font-size:var(--fs-sm);min-height:40px;padding:9px 13px 9px 30px;
  }
  .nav-sub .nav-link.active::before{left:12px}
  .nav-sub::before{left:14px}
  .side-projects a{font-size:var(--fs-sm);min-height:38px;padding:8px 13px 8px 30px}
}

/* ── Phone ──────────────────────────────────────────────────────────────── */
@media (max-width:640px){
  /* Prose has the screen width and no more; the cap above would do nothing
     here but is stated so the two cases are not accidentally different. */
  .prewrap,.empty-state p,.restricted-note{max-width:none}
  .dh-title{overflow-wrap:anywhere}
}

/* ══════════════════════════════════════════════════════════════════════════
   ONE MENU SIZE — sections and entries matched
   ══════════════════════════════════════════════════════════════════════════
   Until now a section heading was 0.765rem and the entries under it 0.72rem.
   Two sizes four hundredths of a rem apart is the worst of both: too close to
   read as a deliberate hierarchy, far enough to look like a mistake.

   So the whole menu is one size — the smaller of the two — and the hierarchy
   is carried by everything except size:

     · **weight**  section 640, entry 540. The strongest of the four cues, and
       the one that survives at any size.
     · **ink**     section brighter, entry a step back. Two levels, not three.
     · **indent**  an entry sits 26px in, against the hairline rail; a section
       sits at 10px with its icon.
     · **icon**    sections have one, entries do not. On its own that is
       already enough to tell them apart at a glance.

   Differentiating by weight and position rather than by size is also what
   keeps the column from looking ragged: every row is now the same height and
   the same size, so the left edge and the baseline grid both read as straight
   lines instead of a staircase.                                             */

/* One size for every row in the sidebar. */
.nav a,
.nav-group > summary,
.side-public,
.nav-sub .nav-link,
.side-projects a{
  font-size:.72rem;
  letter-spacing:.022em;
}

/* Sections: the heavier, brighter of the two levels. */
.nav-group > summary,
.nav a,
.side-public{
  font-weight:640;
  color:#cfe0d7;
  min-height:29px;
  padding:5px 10px;
}
.nav-group > summary{opacity:1}

/* Entries: same size, lighter, a step back, indented to the rail. */
.nav-sub .nav-link,
.side-projects a{
  font-weight:540;
  color:#a9c2b6;
  min-height:26px;
  padding:4px 10px 4px 26px;
}
.nav-sub .nav-link:hover,
.side-projects a:hover{color:#fff;background:rgba(255,255,255,.075)}

/* Active stays unmistakable at this size: full-strength ink, the heaviest
   weight in the column, and — for an entry — the dot on the rail. */
.nav a.active,
.nav-group > summary.active{color:#fff;font-weight:700}
.nav-sub .nav-link.active,
.side-projects a.active{color:#fff;font-weight:700}

/* The icon is now doing real work as a section marker, so it holds its own
   weight rather than sitting at 62% opacity. */
.nav-ic{width:14px;height:14px;flex-basis:14px;opacity:.7}
.nav a.active .nav-ic,
.nav-group > summary.active .nav-ic{opacity:1}

.nav-label{gap:8px}
.nav a,.nav-group > summary,.side-public{gap:8px}

/* ── Touch ──────────────────────────────────────────────────────────────── */
/* Matched there too — the point of one size is that it is one size. The rows
   grow to a thumb-sized target; the type grows with them, together. */
@media (pointer:coarse){
  .nav a,
  .nav-group > summary,
  .side-public,
  .nav-sub .nav-link,
  .side-projects a{font-size:var(--fs-sm)}

  .nav a,.nav-group > summary,.side-public{min-height:44px;padding:10px 13px}
  .nav-sub .nav-link,.side-projects a{min-height:40px;padding:9px 13px 9px 30px}
  .nav-ic{width:17px;height:17px;flex-basis:17px}
}

/* ══════════════════════════════════════════════════════════════════════════
   CRYSTAL — frosted glass, and a light sidebar
   ══════════════════════════════════════════════════════════════════════════
   The sidebar was a deep green panel and the cards were flat white with
   `--card-blur:none`, so "glass" was a class name rather than a material.
   This makes it real, and makes the menu part of the same surface as the page
   instead of a dark slab beside it.

   Two rules govern the whole thing, and both are about it staying *fast*:

   1. **Blur is chrome-only.** `backdrop-filter` forces the browser to
      composite a separate layer and re-sample what is behind it on every
      frame. On the four surfaces that are always on screen — sidebar, top bar,
      tab bar, modal — that is four layers and costs nothing measurable. On
      thirty cards in a register it is thirty layers and a scroll that stutters
      on exactly the site-office hardware this runs on. Cards therefore get
      translucency, a hairline and an inner highlight, which reads as glass
      over the page's own gradient without sampling anything.

   2. **Every colour is a token.** A light sidebar means the menu ink flips
      from light-on-dark to dark-on-light — and it must flip *back* in the dark
      theme. Naming the six sidebar roles here is what lets one set of rules
      serve both, instead of a `[data-theme="dark"]` override per line.

   Contrast is held to WCAG AA against the *lower* bound of each translucent
   ground, not against the white it would be if the page behind it were blank.
   ══════════════════════════════════════════════════════════════════════════ */

:root,
:root[data-theme="light"]{
  /* ── Glass ──────────────────────────────────────────────────────────── */
  /* Saturation as well as blur: a plain blur of a muted page reads grey and
     dead. Lifting saturation is what makes frosted glass look like glass. */
  --glass-blur: blur(18px) saturate(150%);
  --glass-bg:   rgba(255,255,255,.66);
  --glass-brd:  rgba(255,255,255,.85);
  --glass-ring: rgba(15,61,135,.13);
  /* A light top edge and a soft bottom: how a real pane catches a room. */
  --glass-sheen: inset 0 1px 0 rgba(255,255,255,.9);
  --glass-drop:  0 6px 22px -10px rgba(11,37,69,.22), 0 1px 3px rgba(11,37,69,.05);

  --card-bg:   rgba(255,255,255,.74);
  --card-blur: none;                    /* see rule 1 above */

  /* ── Sidebar roles ──────────────────────────────────────────────────── */
  --side-bg:        linear-gradient(180deg, rgba(255,255,255,.80), rgba(247,251,248,.68));
  --side-ink:       #16241d;            /* 13.4:1 on the lower bound */
  --side-ink-dim:   #4a6058;            /*  6.1:1 — sub-entries          */
  --side-ink-quiet: #6b8177;            /*  4.6:1 — section labels       */
  --side-hover:     rgba(15,61,135,.075);
  --side-active-bg: rgba(15,61,135,.11);
  --side-active-ink:#0b2e68;
  --side-rail:      var(--army);
  --side-brd:       rgba(15,61,135,.12);
  --side-hairline:  rgba(15,61,135,.16);
}

:root[data-theme="dark"]{
  --glass-blur: blur(18px) saturate(135%);
  --glass-bg:   rgba(16,26,46,.62);
  --glass-brd:  rgba(255,255,255,.09);
  --glass-ring: rgba(255,255,255,.06);
  --glass-sheen: inset 0 1px 0 rgba(255,255,255,.07);
  --glass-drop:  0 8px 26px -12px rgba(0,0,0,.6), 0 1px 3px rgba(0,0,0,.4);

  --card-bg:   rgba(18,30,52,.70);
  --card-blur: none;

  --side-bg:        linear-gradient(180deg, rgba(12,20,38,.82), rgba(8,14,26,.74));
  --side-ink:       #e4ecf8;
  --side-ink-dim:   #a8b8cc;
  --side-ink-quiet: #8598b1;
  --side-hover:     rgba(255,255,255,.07);
  --side-active-bg: rgba(224,164,74,.16);
  --side-active-ink:#ffffff;
  --side-rail:      var(--gold);
  --side-brd:       rgba(255,255,255,.08);
  --side-hairline:  rgba(255,255,255,.11);
}

/* ── The glass material ─────────────────────────────────────────────────── */
.glass,
.stat,
.panel,
.result-card,
.notif-row,
.org-card,
.kl-card{
  background:var(--card-bg);
  border:1px solid var(--glass-brd);
  box-shadow:var(--glass-sheen), var(--glass-drop), 0 0 0 1px var(--glass-ring);
}

/* The four surfaces that earn a real blur. */
.side,
.topbar,
.tabbar,
.modal{
  backdrop-filter:var(--glass-blur);
  -webkit-backdrop-filter:var(--glass-blur);
}

/* ── Sidebar ────────────────────────────────────────────────────────────── */
.side{
  background:var(--side-bg);
  border-right:1px solid var(--side-brd);
  box-shadow:var(--glass-sheen);
}

.nav a,
.nav-group > summary,
.side-public{color:var(--side-ink)}

.nav a:hover,
.nav-group > summary:hover,
.side-public:hover{background:var(--side-hover);color:var(--side-active-ink)}

.nav a.active,
.nav-group > summary.active{
  background:var(--side-active-bg);
  color:var(--side-active-ink);
  box-shadow:inset 2px 0 0 var(--side-rail);
}

.nav-sub .nav-link,
.side-projects a{color:var(--side-ink-dim)}
.nav-sub .nav-link:hover,
.side-projects a:hover{background:var(--side-hover);color:var(--side-active-ink)}
.nav-sub .nav-link.active,
.side-projects a.active{
  color:var(--side-active-ink);
  background:var(--side-active-bg);
}
.nav-sub .nav-link.active::before{background:var(--side-rail)}
.nav-sub::before{background:var(--side-hairline)}

.nav-section,
.side-projects-head{color:var(--side-ink-quiet)}
.sp-empty,
.sp-serial{color:var(--side-ink-quiet)}
.nav-ic{opacity:.7}
.nav a.active .nav-ic,
.nav-group > summary.active .nav-ic{opacity:1;color:var(--side-rail)}

.brand-text{color:var(--side-ink)}
.brand-text small{color:var(--side-ink-quiet)}
.rail-btn,
.drawer-close{color:var(--side-ink-dim)}
.rail-btn:hover,
.drawer-close:hover{background:var(--side-hover);color:var(--side-ink)}

/* The chevron on a section heading, and its rotation, follow the ink. */
.nav-group > summary::after{color:var(--side-ink-quiet)}

.nav::-webkit-scrollbar-thumb{background:var(--side-hairline)}
.nav{scrollbar-color:var(--side-hairline) transparent}

/* ── Top bar ──────────────────────────────────────────────────────────────
   The glass material, and one border rather than two.

   The block near the top of this file gives the bar a border on all four
   sides; this one used to add a `border-bottom` as well, so the bottom edge
   was drawn twice in two different colours and read as a slightly heavier,
   slightly wrong line. One hairline all the way round, and the shadow does the
   lifting instead.

   The inset highlight is what makes frosted glass look like glass: a light
   edge along the top where a real pane would catch the room. */
.topbar{
  background:var(--glass-bg);
  border:1px solid var(--side-brd);
  box-shadow:
    var(--glass-sheen),
    inset 0 1px 0 color-mix(in srgb, #fff 55%, transparent);
}
.tabbar{
  background:var(--glass-bg);
  border-top:1px solid var(--side-brd);
}

/* ── Inputs read as glass too, so a form is not a row of opaque slabs ───── */
.inp,input[type="text"],input[type="search"],input[type="number"],
input[type="date"],input[type="time"],input[type="email"],input[type="url"],
input[type="password"],select,textarea{
  background:var(--card-bg);
  border-color:var(--glass-ring);
}
.inp:focus,input:focus,select:focus,textarea:focus{
  background:var(--surface);
}

/* ── Motion ─────────────────────────────────────────────────────────────── */
/* Interactive surfaces settle rather than snap. Only compositor-friendly
   properties are animated — opacity, transform, background — so none of this
   triggers layout. */
.glass,.stat,.panel,.result-card,.notif-row,.org-card{
  transition:box-shadow var(--dur-2) var(--ease),
             border-color var(--dur-2) var(--ease),
             background var(--dur-2) var(--ease);
}
a.stat:hover,.result-card:hover,.notif-row:hover,.org-card:hover{
  border-color:var(--army-300);
  box-shadow:var(--glass-sheen), var(--e3), 0 0 0 1px var(--glass-ring);
}

/* ── Falling back ───────────────────────────────────────────────────────── */
/* Someone who has asked their system to reduce transparency has asked for a
   reason — usually legibility. Give them opaque surfaces and no blur. */
@media (prefers-reduced-transparency: reduce){
  :root,:root[data-theme="light"]{
    --glass-bg:var(--surface);--card-bg:var(--surface);
    --side-bg:var(--surface-2);
  }
  :root[data-theme="dark"]{
    --glass-bg:var(--surface);--card-bg:var(--surface);
    --side-bg:var(--surface-2);
  }
  .side,.topbar,.tabbar,.modal{backdrop-filter:none;-webkit-backdrop-filter:none}
}

/* A browser without backdrop-filter gets a more opaque ground, or the chrome
   would be a washed-out rectangle with the page legible straight through it. */
@supports not (backdrop-filter: blur(1px)){
  .side{background:var(--surface-2)}
  .topbar,.tabbar{background:var(--surface)}
}

/* ── Phone ──────────────────────────────────────────────────────────────── */
@media (max-width:1024px){
  /* The drawer sits over the page rather than beside it, so it needs to be
     opaque enough to read against whatever it happens to cover. */
  .side{
    background:linear-gradient(180deg,
      color-mix(in srgb, var(--surface) 96%, transparent),
      color-mix(in srgb, var(--surface-2) 94%, transparent));
    box-shadow:var(--e4);
  }
}

/* ══════════════════════════════════════════════════════════════════════════
   BRAND PALETTE — navy, verified
   ══════════════════════════════════════════════════════════════════════════
   The ramp was the green of the national flag. It is now the deep navy of the
   requested reference design, on a white page, with the ramp rebuilt around
   it. The token is still called `--army` because four thousand lines refer to
   it by that name; only the value moved.

   Note what this costs: the Bangladesh Army's green and gold were the identity
   this interface carried, and the emblem is still built on them. That is a
   branding decision, made deliberately, not an accident of a refactor.

   Every step was measured before it was adopted, because the header of this
   file promises AA on both themes and a palette change is the easiest way to
   break that promise quietly:

     --army    on white          6.62:1   body text          PASS
     white     on --army         6.62:1   button label       PASS
     white     on --army-2       9.50:1   deep button        PASS
     --army    on the dark page  5.56:1   link in dark theme PASS

   Two things deliberately did **not** change:

   * **The gold.** No shade of it clears 4.5:1 on white — #d8862a is 2.98,
     and going darker enough to pass stops looking like gold. The existing
     split is already the right answer: `--gold` stays decorative (rules,
     icons, the active rail) and `--gold-ink` #6d520a carries any gold-coloured
     *text*. Adding a "richer" gold would have meant unreadable labels.
   * **The red.** The flag red #f42a41 measures 4.00:1 on white — fine for a
     large heading, not for the 0.7rem badge text it would land on. #b3121f
     stays for anything that has to be read; the flag red would only ever be
     decorative, and there is nowhere here that wants it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The glass shell's own palette layer. It restates the ramp *after* the block
   at the top of the file, so it — not that one — is what the page actually
   uses. Both have to move together; changing only the first is why the cards
   turned navy while the page behind them stayed green. */
:root,
:root[data-theme="light"]{
  --army-50:   #eef5fd;
  --army-100:  #d8e7fa;
  --army-200:  #aecdf4;
  --army-300:  #6ea6ea;
  --army-400:  #2f70cf;
  --army:      #0f3d87;
  --army-2:    #0b2e68;
  --army-3:    #082246;

  /* White page. --surface-2/-3 keep a whisper of grey-blue because they are
     the fills — hover rows, chips, inset panels — and on a white page a white
     fill has no edge. */
  --bg-0:      #ffffff;
  --bg-1:      #ffffff;
  --surface-2: #f5f8fc;
  --surface-3: #eaf0f7;
  --brd:       rgba(15,61,135,.16);
  --brd-soft:  rgba(11,37,69,.10);
  --info-bg:   #e8f1fb;   --info-brd: #b7d0ee;
  --tr-hover:  rgba(15,61,135,.05);

  --page-bg: var(--bg-0);

  --glass-ring:     rgba(15,61,135,.13);
  --side-bg:        linear-gradient(180deg, rgba(255,255,255,.92), rgba(250,252,255,.82));
  --side-hover:     rgba(15,61,135,.075);
  --side-active-bg: rgba(15,61,135,.11);
  --side-active-ink:#0b2e68;
  --side-brd:       rgba(15,61,135,.12);
  --side-hairline:  rgba(15,61,135,.16);
  --focus:          var(--army-400);
}

:root[data-theme="dark"]{
  --army-50:   #0b1626;
  --army-100:  #102138;
  --army-200:  #16304f;
  --army-300:  #1d4a7d;
  --army-400:  #2f6cb8;
  --army:      #4a90e2;
  --army-2:    #2b6bb5;
  --army-3:    #1a4682;
}

/* ══════════════════════════════════════════════════════════════════════════
   FINISH — the craft pass
   ══════════════════════════════════════════════════════════════════════════
   Six things, none of them decoration. Each is a detail that separates an
   interface somebody built from one somebody finished, and in a system that is
   mostly registers of money and dates the first one matters more than the
   other five together.

   1. **Tabular numerals.** Inter's default figures are proportional — a `1` is
      narrower than a `8`. Down a column of contract values that puts every
      digit in a different place and the column reads as ragged, however
      carefully it is aligned. `tabular-nums` gives every digit the same
      advance width, so figures stack. This is the single largest visual
      improvement available to this application and it costs one property.

   2. **Optical tracking.** Type needs *less* letter-spacing as it gets larger
      and more as it gets smaller — a heading set at the same tracking as body
      copy looks loose, and 0.7rem set at heading tracking looks cramped. The
      scale below tightens as size climbs.

   3. **One light source.** Shadows were written per component, so different
      cards implied light from different directions. Every elevation here is a
      vertical offset with the same tint, which is what makes a surface read as
      lifted rather than outlined.

   4. **A real focus ring.** `outline: 2px solid` sits square on a rounded
      control and clips at the edges. A ring that follows the radius, sits off
      the edge and carries a contrasting inner halo is visible on any ground —
      and this is an accessibility feature before it is a beautiful one.

   5. **Selection, caret and scrollbar.** Three details nobody names and
      everybody notices when they are the browser default blue on an otherwise
      green interface.

   6. **Restraint on press.** A control that settles a half-pixel under the
      finger feels physical. Anything more reads as a toy.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Figures that stack ──────────────────────────────────────────────── */
/* Applied where numbers are *compared* — tables, stats, ledgers, counters —
   and deliberately not to running prose, where tabular figures look gappy. */
table,
.stat .v,
.kpi .num,
.progress + span,
.cell-sub,
.pv-kpi .v,
.rank-chip,
.notif-dot,
.pager-count,
.kv-grid .v,
.where-code,
.rc-ref,
.mono,
time,
.panel-list .cell-sub{
  font-variant-numeric:tabular-nums lining-nums;
  font-feature-settings:"tnum" 1, "lnum" 1;
}

/* A money or count column is read by comparing magnitudes, so it aligns right
   and the digits sit directly under one another. */
table td.right,
table th.right{text-align:right;font-variant-numeric:tabular-nums}

/* ── 2. Optical tracking ────────────────────────────────────────────────── */
h1,.dh-title,.kpi .num,.stat .v,.where-code{letter-spacing:-.011em}
h2,.kl-card strong{letter-spacing:-.006em}
.section-title,.kv-grid .k,.panel-head{letter-spacing:.055em}
.badge,.cell-sub,.rc-kind{letter-spacing:.028em}

/* Inter's contextual alternates and proper ligatures. Off by default in some
   engines; they are what make the face look like itself. */
body{font-feature-settings:"cv05" 1, "calt" 1, "liga" 1;text-rendering:optimizeLegibility}

/* ── 3. One light source ────────────────────────────────────────────────── */
/* Every offset positive-Y, every tint the same green-black. A shadow that
   disagrees with its neighbours is what makes a page look assembled. */
:root,:root[data-theme="light"]{
  --e1: 0 1px 2px rgba(11,37,69,.05);
  --e2: 0 2px 5px rgba(11,37,69,.07), 0 1px 2px rgba(11,37,69,.04);
  --e3: 0 8px 18px -8px rgba(11,37,69,.15), 0 2px 5px rgba(11,37,69,.05);
  --e4: 0 22px 46px -16px rgba(11,37,69,.22), 0 4px 12px rgba(11,37,69,.07);
  --glass-drop: 0 6px 20px -12px rgba(11,37,69,.20), 0 1px 2px rgba(11,37,69,.05);
}
:root[data-theme="dark"]{
  --e1: 0 1px 2px rgba(0,0,0,.34);
  --e2: 0 2px 6px rgba(0,0,0,.40), 0 1px 2px rgba(0,0,0,.28);
  --e3: 0 10px 22px -10px rgba(0,0,0,.62), 0 2px 6px rgba(0,0,0,.36);
  --e4: 0 24px 50px -18px rgba(0,0,0,.72), 0 5px 14px rgba(0,0,0,.44);
  --glass-drop: 0 8px 24px -14px rgba(0,0,0,.66), 0 1px 2px rgba(0,0,0,.4);
}

/* ── 4. The focus ring ──────────────────────────────────────────────────── */
/* `:focus-visible`, not `:focus`: a mouse click on a button should not leave a
   ring behind, but a Tab key must. The inner white halo is what keeps it
   visible when the control sits on the green itself. */
:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{
  outline:2px solid var(--army-400);
  outline-offset:2px;
  box-shadow:0 0 0 4px color-mix(in srgb, var(--army-400) 22%, transparent);
  border-radius:inherit;
}
.btn:focus-visible{outline-offset:3px}
/* A sidebar row's ring follows its own corner radius rather than the square
   the browser would draw. */
.nav a:focus-visible,
.nav-group > summary:focus-visible,
.nav-sub .nav-link:focus-visible{outline-offset:-2px;box-shadow:none}

/* ── 5. Selection, caret, scrollbar ─────────────────────────────────────── */
::selection{background:color-mix(in srgb, var(--army) 22%, transparent);color:var(--txt-strong)}
input,textarea,select{caret-color:var(--army)}

/* A scrollbar the same weight as the interface rather than the operating
   system's. Firefox takes the two-value form; WebKit needs the pseudo-element. */
*{scrollbar-width:thin;scrollbar-color:var(--brd) transparent}
::-webkit-scrollbar{width:9px;height:9px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{
  background:var(--brd);border-radius:var(--r-pill);
  border:2px solid transparent;background-clip:content-box;
}
::-webkit-scrollbar-thumb:hover{background:var(--army-300);background-clip:content-box}

/* ── 6. Restraint on press ──────────────────────────────────────────────── */
.btn:active,.icon-btn:active{transform:translateY(.5px)}
a.stat:active,.result-card:active,.notif-row:active,.org-card:active,
.kl-card:active{transform:translateY(.5px)}

/* ── Tables ─────────────────────────────────────────────────────────────── */
/* Hairlines and a hover row rather than zebra striping: stripes fight the
   translucent card behind them, and on a register of forty rows they are
   noise that carries no information. */
.register-table thead th{
  border-bottom:1px solid var(--brd);
  background:transparent;
  font-weight:700;text-transform:uppercase;color:var(--muted);
}
.register-table tbody tr{border-bottom:1px solid var(--brd-soft)}
.register-table tbody tr:last-child{border-bottom:0}
.register-table tbody tr:hover{background:var(--tr-hover)}
.register-table td a{font-weight:650}

/* The first cell of a row is its identity, so it carries the weight. */
.register-table tbody td:first-child{font-weight:600;color:var(--txt-strong)}

/* ── Section rule ───────────────────────────────────────────────────────── */
/* The gold is used once per section and nowhere else, which is what keeps it
   reading as an accent rather than as a second brand colour. */
.section-title::before{
  height:2px;width:18px;border-radius:2px;
  background:linear-gradient(90deg,var(--gold),var(--army));
}

/* ── Motion ─────────────────────────────────────────────────────────────── */
/* Somebody who has asked for less motion has usually asked because motion
   makes them ill. Honour it everywhere, not on the sidebar alone. */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
}

/* ── Print ──────────────────────────────────────────────────────────────── */
/* A register printed for a file should be the register, not the application. */
@media print{
  .side,.topbar,.tabbar,.nav-scrim,.register-search,.form-actions,
  .dh-actions,.pager,.skip-link,.app-footer{display:none !important}
  .main{padding:0;max-width:none}
  .glass,.stat,.panel{
    background:#fff !important;box-shadow:none !important;
    border:1px solid #bbb !important;backdrop-filter:none !important;
  }
  .register-table{font-size:9pt}
  a[href]::after{content:""}
}

/* ══════════════════════════════════════════════════════════════════════════
   SEARCHABLE SELECT
   ══════════════════════════════════════════════════════════════════════════
   A second face on a native <select> — see static/js/select.js. The native
   element stays in the DOM and stays the thing that submits; it is moved
   out of sight rather than removed, so labels, `required` and server-side
   validation all still point at something real.

   Touch devices never get this: the platform's own picker is a full-screen
   sheet that beats anything a page can draw with a thumb. `select.js` returns
   early on a coarse pointer, so these rules simply never apply there. */
.ss{position:relative;display:block;width:100%}

/* Not display:none — a hidden field is skipped by some browsers' form
   validation UI, and the browser then cannot point at the control it is
   complaining about. Clipped to nothing instead, which keeps it focusable
   enough to be reported on. */
.ss-native{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);border:0;
}

.ss-btn{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  width:100%;text-align:left;cursor:pointer;font-family:inherit;
  background:var(--input-bg);
}
.ss-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ss-value.is-placeholder{color:var(--muted-2)}
.ss-caret{
  flex:0 0 auto;width:0;height:0;border-left:5px solid transparent;
  border-right:5px solid transparent;border-top:6px solid currentColor;
  opacity:.55;transition:transform var(--dur-1) ease;
}
.ss.is-open .ss-caret{transform:rotate(180deg)}
.ss.is-open .ss-btn{
  border-color:color-mix(in srgb, var(--army) 45%, transparent);
  box-shadow:0 0 0 3px color-mix(in srgb, var(--army) 12%, transparent);
}

.ss-panel{
  position:absolute;left:0;right:0;top:calc(100% + 6px);z-index:70;
  padding:6px;border-radius:var(--r-lg);
  background:var(--surface);border:1px solid var(--brd);box-shadow:var(--e4);
}
/* Opened upward when there is no room below. A fifty-row list unfurling off
   the bottom of the viewport is a list nobody can read. */
.ss.ss-up .ss-panel{top:auto;bottom:calc(100% + 6px)}

.ss-filter{
  width:100%;margin-bottom:6px;height:36px;padding:0 12px;
  border-radius:var(--r-md);border:1px solid var(--brd);
  background:var(--surface-2);font:inherit;font-size:.86rem;color:var(--txt);
}
/* :focus-visible, not :focus — the stylesheet's own contract, enforced by
   core.tests.FinishTests. Exact rather than a workaround here: a
   text-entry element always matches :focus-visible when it is focused,
   however the focus arrived, so this fires on the programmatic focus the
   panel gives it on open as well as on a click. */
.ss-filter:focus-visible{outline:none;border-color:var(--army-300);background:var(--surface)}

.ss-list{max-height:min(46vh,320px);overflow-y:auto;overscroll-behavior:contain}
.ss-row{
  padding:8px 11px;border-radius:var(--r-md);cursor:pointer;
  font-size:.88rem;color:var(--txt);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* `.hi` is the keyboard cursor and `:hover` the mouse. They look identical on
   purpose — a keyboard path that looks different reads as second class. */
.ss-row:hover,.ss-row.hi{background:var(--surface-2)}
.ss-row.on{color:var(--army);font-weight:700;background:var(--army-50)}
.ss-empty{margin:0;padding:12px;text-align:center;color:var(--muted);font-size:.82rem}

@media (prefers-reduced-motion:reduce){ .ss-caret{transition:none} }

/* ── Menu admin: bulk visibility ──────────────────────────────────────────
   Revealed by menu-admin.js. Ships `hidden` so a browser without JavaScript
   never shows a control that would do nothing. */
.menu-bulk{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-bottom:14px;padding:10px 14px;
  background:var(--surface-2);border:1px solid var(--brd-soft);
  border-radius:var(--r-lg);
}
.menu-bulk-label{font-size:.8rem;font-weight:700;color:var(--txt-strong)}
.menu-bulk-count{margin-left:auto;font-size:.78rem;color:var(--muted)}
/* The header tick sits above the word it governs. */
.menu-table thead .th-sub{display:block;font-size:.62rem;opacity:.75;margin-top:2px}

/* ── Master data ──────────────────────────────────────────────────────────
   A row of one input and a button. Its own form, so Enter in the field means
   "add this one" rather than "save the whole list". */
.master-add{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:12px}
.master-add input{flex:1 1 220px;min-width:0}
.master-removed{margin-top:12px;font-size:.84rem}
.master-removed summary{cursor:pointer;color:var(--muted);font-weight:650}
.master-removed ul{list-style:none;margin:8px 0 0;padding:0;display:grid;gap:6px}
.master-removed li{
  display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding:7px 11px;border-radius:var(--r-md);background:var(--surface-2);
}
.master-removed form{margin:0}

/* ══════════════════════════════════════════════════════════════════════════
   MASTER DATA
   ══════════════════════════════════════════════════════════════════════════
   Every dropdown in the project as a grid of closed cards. Sixteen editable
   lists and sixty-five system ones opened flat is a page nobody scrolls to the
   end of; closed, the whole vocabulary fits on a screen and you open the one
   you came for.

   <details>, so it works without JavaScript, is keyboard-operable for free,
   and the browser's own find-in-page can open a card to reveal a match.

   The depth here is doing a job, not decorating. This page has three planes —
   the page, the card, and the row well inside an open card — and a reader has
   to know which one they are editing. Two stacked shadows per card: a tight
   1px contact shadow that reads as the card touching the page, and a wide soft
   one that reads as height. A single blurred shadow gives neither. */

/* ── The header: three counts and a sentence ──────────────────────────────
   The counts answer "how much of this system is configurable", which is the
   first thing anybody arriving here wants to know. */
.md-head{
  display:flex;align-items:center;gap:clamp(16px,3vw,34px);flex-wrap:wrap;
  padding:18px 22px;margin-bottom:16px;
  background:linear-gradient(180deg, var(--surface) 0%, var(--surface-2) 100%);
  border:1px solid var(--brd-soft);border-radius:var(--r-xl);
  box-shadow:0 1px 2px rgb(15 23 42 / .05), 0 10px 26px -18px rgb(15 23 42 / .28);
}
.md-stats{display:flex;gap:clamp(16px,3vw,32px);flex-wrap:wrap}
.md-stat{display:flex;flex-direction:column;line-height:1.1}
.md-stat strong{font-size:1.55rem;font-weight:800;color:var(--army);letter-spacing:-.01em}
.md-stat span{
  font-size:.68rem;color:var(--muted);text-transform:uppercase;
  letter-spacing:.7px;font-weight:700;margin-top:3px;
}
.md-stat.is-muted strong{color:var(--muted)}
.md-note{flex:1 1 280px;margin:0;font-size:.83rem;color:var(--muted);line-height:1.55}
.md-hint{margin:-4px 0 14px;font-size:.82rem;color:var(--muted);line-height:1.55;max-width:76ch}

/* ── The filter bar ───────────────────────────────────────────────────────
   Sticky, because the point of it is to be reachable from wherever the scroll
   has ended up. Revealed by master.js — see that file. */
.md-bar{
  position:sticky;top:calc(var(--topbar-h) + 6px);z-index:5;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:10px 12px;margin-bottom:18px;
  background:color-mix(in srgb, var(--surface) 88%, transparent);
  -webkit-backdrop-filter:saturate(1.5) blur(10px);
  backdrop-filter:saturate(1.5) blur(10px);
  border:1px solid var(--brd-soft);border-radius:var(--r-pill);
  box-shadow:0 1px 2px rgb(15 23 42 / .05), 0 8px 22px -16px rgb(15 23 42 / .3);
}
.md-search{flex:1 1 240px;min-width:0;display:flex}
.md-search input{
  width:100%;min-width:0;height:38px;
  border-radius:var(--r-pill);padding-inline:16px;
}
.md-showing{
  font-size:.74rem;color:var(--muted);font-weight:700;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}

/* ── The grid of cards ──────────────────────────────────────────────────── */
.md-grid{
  display:grid;gap:12px;margin-bottom:22px;
  grid-template-columns:repeat(auto-fill,minmax(320px,1fr));
  align-items:start;
}
/* An open card takes the full row: its contents are a table of inputs, and a
   320px column would wrap every one of them. */
.md-card[open]{grid-column:1 / -1}

.md-card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--brd-soft);
  border-radius:var(--r-lg);
  box-shadow:0 1px 2px rgb(15 23 42 / .05), 0 6px 18px -14px rgb(15 23 42 / .3);
  overflow:hidden;
  transition:box-shadow var(--dur-2) var(--ease),
             border-color var(--dur-1) ease,
             transform var(--dur-2) var(--ease);
}
/* The accent edge. Invisible until the card is hovered or open, so a closed
   grid stays quiet and the one being worked in is unmistakable. */
.md-card::before{
  content:"";position:absolute;inset:0 auto 0 0;width:3px;
  background:var(--army);opacity:0;
  transition:opacity var(--dur-2) var(--ease);
}
.md-card:hover{
  border-color:var(--brd);transform:translateY(-2px);
  box-shadow:0 1px 2px rgb(15 23 42 / .06), 0 14px 30px -18px rgb(15 23 42 / .38);
}
.md-card:hover::before{opacity:.5}
.md-card[open]{
  border-color:var(--brd);transform:none;
  box-shadow:0 1px 2px rgb(15 23 42 / .06), 0 22px 44px -26px rgb(15 23 42 / .42);
}
.md-card[open]::before{opacity:1}
.md-card.is-system::before{background:var(--muted-2)}
/* The card is what focus should read as landing on; ringing the summary alone
   draws a box round the header of a card that has grown to fill the row. */
.md-card > summary:focus-visible{outline:none}
.md-card:has(> summary:focus-visible){
  border-color:var(--army);
  box-shadow:0 0 0 3px var(--army-50), 0 14px 30px -18px rgb(15 23 42 / .38);
}

.md-card > summary{
  display:flex;align-items:center;gap:12px;justify-content:space-between;
  padding:14px 16px;cursor:pointer;list-style:none;min-height:var(--tap);
  transition:background var(--dur-1) ease;
}
.md-card > summary:hover{background:var(--surface-2)}
.md-card > summary::-webkit-details-marker{display:none}
.md-card > summary::after{
  content:"";flex:0 0 auto;width:0;height:0;order:3;
  border-left:5px solid transparent;border-right:5px solid transparent;
  border-top:6px solid currentColor;opacity:.45;
  transition:transform var(--dur-2) var(--ease);
}
.md-card[open] > summary{background:var(--surface-2)}
.md-card[open] > summary::after{transform:rotate(180deg)}
.md-card-head{display:flex;flex-direction:column;gap:2px;min-width:0}
.md-card-name{font-weight:700;font-size:.94rem;color:var(--txt-strong);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.md-card-key{font-size:.68rem;color:var(--muted-2);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.md-card-count{
  flex:0 0 auto;min-width:30px;padding:3px 9px;border-radius:var(--r-pill);
  background:var(--army-50);color:var(--army);
  font-size:.74rem;font-weight:800;text-align:center;
  font-variant-numeric:tabular-nums;
}
.md-card.is-system .md-card-count{background:var(--surface-3);color:var(--muted)}

.md-card-body{padding:2px 16px 16px;border-top:1px solid var(--brd-soft)}
.md-desc{margin:12px 0;font-size:.82rem;color:var(--muted);line-height:1.5}

/* ── The rows ─────────────────────────────────────────────────────────────
   One option per line: switch, name, stored value, order, remove. A grid
   rather than a table so it collapses to two lines on a phone without a
   horizontal scrollbar.

   The well is inset — a shadow pressed inward rather than cast outward — so
   the list reads as recessed into the card rather than as a third stack of
   cards floating above it. */
.md-rows{
  display:grid;gap:4px;margin-top:12px;padding:6px;
  background:var(--surface-2);border-radius:var(--r-md);
  box-shadow:inset 0 1px 3px rgb(15 23 42 / .06);
}
.md-row{
  display:grid;gap:8px;align-items:center;
  grid-template-columns:auto 1fr auto 72px auto;
  padding:5px 6px;border-radius:var(--r-sm);
  background:var(--surface);
  border:1px solid transparent;
  transition:border-color var(--dur-1) ease, box-shadow var(--dur-1) ease;
}
.md-row:hover{border-color:var(--brd-soft);box-shadow:0 1px 3px rgb(15 23 42 / .07)}
/* Dimmed, not hidden: the row is still editable, and still has to be readable
   while somebody decides whether to put it back. */
.md-row.is-off{opacity:.6}
.md-row.is-off .md-label{text-decoration:line-through;text-decoration-color:var(--muted-2)}
.md-none{margin:6px 8px;font-size:.83rem}

/* A switch, not a bare tick. This governs whether an option appears in a
   menu — a state worth reading at a glance from across a desk, and a target
   worth hitting with a finger on a wall panel. */
.md-switch{position:relative;display:flex;align-items:center;justify-content:center;
  width:38px;height:30px;cursor:pointer}
.md-switch input{position:absolute;opacity:0;width:100%;height:100%;margin:0;cursor:pointer}
.md-switch-track{
  position:relative;width:32px;height:18px;border-radius:var(--r-pill);
  background:var(--surface-3);border:1px solid var(--brd);
  transition:background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.md-switch-track::after{
  content:"";position:absolute;top:1px;left:1px;width:14px;height:14px;
  border-radius:50%;background:#fff;
  box-shadow:0 1px 2px rgb(15 23 42 / .35);
  transition:transform var(--dur-2) var(--ease);
}
.md-switch input:checked + .md-switch-track{background:var(--army);border-color:var(--army)}
.md-switch input:checked + .md-switch-track::after{transform:translateX(14px)}
.md-switch input:focus-visible + .md-switch-track{
  outline:2px solid var(--army);outline-offset:2px;
}

.md-label{width:100%;min-width:0}
.md-stored{
  font-size:.7rem;color:var(--muted-2);max-width:150px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.md-order{width:100%;text-align:center;font-variant-numeric:tabular-nums}
.md-x{
  width:32px;height:32px;border-radius:var(--r-md);cursor:pointer;
  border:1px solid transparent;background:transparent;color:var(--muted);
  font-size:.9rem;line-height:1;
  transition:background var(--dur-1) ease, color var(--dur-1) ease,
             border-color var(--dur-1) ease;
}
.md-x:hover{background:var(--bad-bg);border-color:var(--bad-brd);color:var(--bad)}

.md-actions{margin-top:12px}
.md-danger{margin-top:14px;padding-top:12px;border-top:1px dashed var(--brd-soft)}

.md-readonly{list-style:none;margin:12px 0 0;padding:6px;display:grid;gap:3px;
  background:var(--surface-2);border-radius:var(--r-md);
  box-shadow:inset 0 1px 3px rgb(15 23 42 / .06)}
.md-readonly li{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:7px 10px;border-radius:var(--r-sm);background:var(--surface);
  font-size:.84rem;
}

/* Adding a list is the one action here that cannot destroy anything, so it
   gets a panel of its own rather than a row at the bottom of somebody else's. */
.md-new{
  padding:16px 18px;margin-bottom:22px;
  background:var(--surface);border:1px solid var(--brd-soft);
  border-radius:var(--r-lg);
  box-shadow:0 1px 2px rgb(15 23 42 / .05), 0 6px 18px -14px rgb(15 23 42 / .3);
}
.md-blank{padding:26px;text-align:center;grid-column:1 / -1}
.md-nomatch{
  grid-column:1 / -1;margin:0;padding:22px;text-align:center;
  color:var(--muted);font-size:.86rem;
}

/* Phone: the stored value and the order box drop to a second line rather than
   squeezing the name they describe into nothing. */
@media (max-width:640px){
  .md-grid{grid-template-columns:1fr}
  .md-row{grid-template-columns:auto 1fr auto}
  .md-stored{grid-column:2;font-size:.66rem}
  .md-order{grid-column:3;width:64px}
  .md-head{gap:18px;padding:16px}
  .md-bar{position:static;border-radius:var(--r-lg)}
}
@media (prefers-reduced-motion:reduce){
  .md-card,.md-card > summary,.md-card > summary::after,
  .md-card::before,.md-row,.md-switch-track,.md-switch-track::after,.md-x{
    transition:none;
  }
  .md-card:hover{transform:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   THE APP
   ══════════════════════════════════════════════════════════════════════════
   Everything under `body.is-app` applies only inside the Android client — the
   User-Agent carries `CPMS-App/`, `core.context_processors.is_app` reads it,
   and `base.html` stamps the class. Chrome on the same handset gets none of
   this, which is the point: a browser has a URL bar, a back gesture and a
   reader who expects a web page. A home-screen icon does not, and a web page
   inside one reads as a website in a frame.

   These are subtractions and re-weightings, not a second stylesheet. A parallel
   set of templates would be a second interface to keep in step, and the one
   that is not being looked at is always the one that rots. */

/* ── The chrome a browser provides, and the app must not duplicate ──────── */

/* No sidebar and no hamburger. The tab bar along the bottom is the navigation;
   a drawer behind a hamburger is the web's answer to not having one. */
body.is-app .side,
body.is-app .nav-scrim,
body.is-app .drawer-btn,
body.is-app .rail-btn,
body.is-app .drawer-close{display:none !important}
body.is-app .shell{display:block}
body.is-app .main{max-width:none;padding-left:var(--gutter);padding-right:var(--gutter)}

/* "Skip to content" is a keyboard affordance for a document. There is no
   keyboard here and nothing to skip past. */
body.is-app .skip-link{display:none}

/* ── The top bar becomes a title bar ─────────────────────────────────────
   Flush to the status bar, no card edge, no shadow: a floating pill with
   rounded corners under a phone's status bar is a web page pretending to be
   an app. A native title bar is part of the window. */
body.is-app .topbar{
  position:sticky;top:0;
  margin:calc(var(--gutter) * -1) calc(var(--gutter) * -1) 14px;
  padding:calc(10px + var(--safe-t)) var(--gutter) 10px;
  border:0;border-radius:0;
  box-shadow:0 1px 0 var(--brd-soft);
  background:var(--surface);
  backdrop-filter:none;-webkit-backdrop-filter:none;
}
body.is-app .topbar h1{font-size:1.22rem;font-weight:800;letter-spacing:-.02em}
/* The accent bar before the title is a web flourish. */
body.is-app .topbar h1::before{display:none}

/* ── Cards go full-bleed ─────────────────────────────────────────────────
   A card inset from both edges of a 390px screen wastes sixteen points either
   side to draw a border nobody needs — the phone's own edge is the frame. */
body.is-app .glass,
body.is-app .card,
body.is-app .panel{
  border-left:0;border-right:0;border-radius:0;
  margin-left:calc(var(--gutter) * -1);
  margin-right:calc(var(--gutter) * -1);
  box-shadow:none;
}
body.is-app .kpi{
  border-radius:var(--r-lg);margin-left:0;margin-right:0;
  border-left:1px solid var(--brd-soft);border-right:1px solid var(--brd-soft);
}
body.is-app .grid,
body.is-app .dash-panels{gap:0}
body.is-app .card + .card,
body.is-app .panel + .panel{border-top:1px solid var(--brd-soft)}

/* ── Touch weight ────────────────────────────────────────────────────────
   Rows are pressed, not clicked. A hover lift is motion nobody asked for on a
   device with no pointer; a press-down is the feedback that means something. */
body.is-app a.card:hover,
body.is-app a.kpi:hover,
body.is-app .stat:hover{transform:none;box-shadow:none}
body.is-app a.card:active,
body.is-app a.kpi:active{background:var(--surface-2);transition:none}

/* ── The tab bar is the navigation, so it is always there ────────────────── */
body.is-app .tabbar{display:flex}
body.is-app .main{padding-bottom:calc(var(--tabbar-h) + var(--safe-b) + 16px)}

/* The footer repeats what the app's own about screen says, on every page. */
body.is-app .app-footer{display:none}

@media (prefers-reduced-motion:reduce){
  body.is-app a.card:active,body.is-app a.kpi:active{transition:none}
}
