/* ═══════════════════════════════════════════════════════════
   FPG Ops — the front door.
   Self-contained: tokens mirror the Support Center admin so the
   two feel like one product, without coupling to its 150KB sheet.
   ═══════════════════════════════════════════════════════════ */
:root{
  --navy:#0B1F3A; --navy-mid:#13304F; --navy-light:#1B4570;
  --gold:#C9A24F; --gold-pale:#FDF6E3;
  --white:#fff; --off-white:#F7F8FB; --border:#E5E9F0;
  --text:#111827; --text-muted:#6B7280; --text-light:#9CA3AF;
  --success:#059669; --error:#DC2626; --warning:#D97706;
  --r-sm:8px; --r-md:14px; --r-lg:18px;
  --t:.2s cubic-bezier(.4,0,.2,1);
}
*{box-sizing:border-box}
body{margin:0;font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  color:var(--text);-webkit-font-smoothing:antialiased}
a{color:inherit}

/* ─── AUTH ─────────────────────────────────────────────── */
.auth-body{min-height:100vh;min-height:100dvh;display:grid;place-items:center;position:relative;overflow:hidden;
  background:linear-gradient(135deg,#050912 0%,#0A1328 50%,#132344 100%);padding:24px}
/* Living background.
   NOTE ON TUNING: these layers are blurred by ~46px, and under that much
   blur a positional shift of a few tens of pixels is literally invisible.
   The first pass ran a 22s cycle and looked static to the eye — the motion
   was real but below the perceptual threshold. What actually reads is
   LUMINANCE: the opacity swings below are what make the field feel alive,
   with the drift providing direction rather than the effect itself. Keep the
   opacity range wide if you retune the timing.
   Three layers drift on deliberately mismatched durations (34s / 46s / 58s)
   so the loop never visibly repeats — it reads as ambient light rather than
   a cycling animation. Only transform and opacity are animated, both
   compositor-friendly, so the blurred layers rasterise once and the page
   stays cheap even left open all day on a leasing-office machine. */
.auth-bg{position:absolute;inset:0;pointer-events:none;overflow:hidden;
  background:radial-gradient(circle at 20% 30%,rgba(201,162,79,.10) 0%,transparent 46%),
             radial-gradient(circle at 80% 70%,rgba(27,69,112,.38) 0%,transparent 56%)}

/* gold light, upper-left */
.auth-bg::before,
.auth-bg::after{content:"";position:absolute;border-radius:50%;
  will-change:transform,opacity;filter:blur(46px)}
.auth-bg::before{
  width:58vw;height:58vw;min-width:420px;min-height:420px;
  left:-10%;top:-16%;
  background:radial-gradient(circle,rgba(201,162,79,.40) 0%,rgba(201,162,79,.14) 45%,transparent 70%);
  animation:ops-drift-a 11s ease-in-out infinite alternate;}
/* deep navy light, lower-right */
.auth-bg::after{
  width:72vw;height:72vw;min-width:500px;min-height:500px;
  right:-16%;bottom:-22%;
  background:radial-gradient(circle,rgba(41,98,158,.55) 0%,rgba(27,69,112,.20) 48%,transparent 72%);
  animation:ops-drift-b 14s ease-in-out infinite alternate;}

@keyframes ops-drift-a{
  0%  {transform:translate3d(0,0,0) scale(.92);          opacity:.35}
  30% {transform:translate3d(8vw,5vh,0) scale(1.26);     opacity:1}
  60% {transform:translate3d(3vw,13vh,0) scale(1.02);    opacity:.55}
  100%{transform:translate3d(14vw,20vh,0) scale(1.18);   opacity:.92}}
@keyframes ops-drift-b{
  0%  {transform:translate3d(0,0,0) scale(1.14);           opacity:1}
  35% {transform:translate3d(-12vw,-8vh,0) scale(.94);     opacity:.48}
  70% {transform:translate3d(-6vw,-17vh,0) scale(1.20);    opacity:.95}
  100%{transform:translate3d(-16vw,-22vh,0) scale(1);      opacity:.60}}

/* A third, very faint sheen crossing the whole field on a longer cycle.
   It lives INSIDE .auth-bg, which clips: when this sat on .auth-body::after
   its inset:-20% escaped the page. body{overflow:hidden} does not clip body's
   own children — the value is propagated to the viewport instead — so the
   sheen made the login page scroll sideways on a phone. */
.auth-sheen{position:absolute;inset:-20%;pointer-events:none;display:block;
  background:radial-gradient(44% 36% at 50% 50%,rgba(255,255,255,.08) 0%,transparent 62%);
  animation:ops-sheen 18s linear infinite;}
@keyframes ops-sheen{
  0%  {transform:translate3d(-22%,12%,0) scale(1)}
  50% {transform:translate3d(20%,-14%,0) scale(1.15)}
  100%{transform:translate3d(-22%,12%,0) scale(1)}}

/* Stillness is the accessible default here — the page is perfectly legible
   without any of this. */
@media (prefers-reduced-motion:reduce){
  .auth-bg::before,.auth-bg::after,.auth-sheen{animation:none!important}}
.auth-card{position:relative;width:100%;max-width:392px;background:var(--white);
  border-radius:var(--r-lg);padding:38px 34px 30px;box-shadow:0 24px 60px rgba(0,0,0,.35)}
.auth-logo{text-align:center;margin-bottom:4px}
.auth-logo-mark{display:block;width:180px;height:40px;margin:0 auto 8px;background-color:var(--navy);
  -webkit-mask:url('../img/fg-logo.png') no-repeat center/contain;
          mask:url('../img/fg-logo.png') no-repeat center/contain}
/* Same lockup as the header, stacked: mark over product name. Letterspaced
   caps at this size read as a plate under the logo rather than a second
   headline competing with "Sign In". */
.auth-logo-name{display:block;font:700 9.5px/1 'Plus Jakarta Sans',sans-serif;letter-spacing:.22em;
  text-transform:uppercase;color:var(--text-muted);text-align:center;margin:11px 0 0}
.auth-logo-seam{display:block;width:34px;height:1px;margin:13px auto 0;
  background:linear-gradient(90deg,rgba(201,162,79,0),var(--gold),rgba(201,162,79,0))}
.auth-title{text-align:center;font:800 22px/1.2 'Plus Jakarta Sans',sans-serif;color:var(--navy);margin:14px 0 0}
.auth-sub{text-align:center;color:var(--text-muted);font-size:.875rem;margin:4px 0 26px}
.auth-label{display:block;font-size:.8125rem;font-weight:600;color:var(--text);margin:0 0 6px}
.auth-input{width:100%;padding:13px;font-size:16px;font-family:inherit;color:var(--text);
  background:var(--white);border:1.5px solid var(--border);border-radius:var(--r-sm);
  margin-bottom:16px;transition:border-color var(--t)}
.auth-input:focus{outline:none;border-color:var(--navy);box-shadow:0 0 0 3px rgba(11,31,58,.08)}
/* Password reveal — same control as the Support Center's login, so the two
   sign-in pages behave identically. Only the icon for the ACTION available
   is shown; the other represents the current state. */
.auth-input-wrap{position:relative}
.auth-input--with-toggle{padding-right:44px}
.auth-pw-toggle{position:absolute;top:calc(50% - 8px);right:6px;transform:translateY(-50%);
  display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;
  padding:0;border:0;border-radius:6px;background:transparent;color:var(--text-muted);
  cursor:pointer;transition:color var(--t),background var(--t)}
.auth-pw-toggle:hover{color:var(--navy);background:rgba(11,31,58,.06)}
.auth-pw-toggle:focus-visible{outline:2px solid var(--gold);outline-offset:1px;color:var(--navy)}
.auth-pw-icon{width:18px;height:18px}
.auth-pw-icon--off{display:none}

.auth-btn{width:100%;margin-top:8px;background:var(--navy);color:#fff;border:none;
  border-radius:var(--r-sm);padding:13px;font-family:'Plus Jakarta Sans',sans-serif;
  font-size:.9375rem;font-weight:700;cursor:pointer;transition:all var(--t);
  box-shadow:0 2px 8px rgba(11,31,58,.35)}
.auth-btn:hover{background:var(--navy-mid);transform:translateY(-1px)}
.auth-error{background:#FEF2F2;border:1px solid #FCA5A5;color:#991B1B;font-size:.8125rem;
  padding:10px 12px;border-radius:var(--r-sm);margin-bottom:16px}
.auth-note{background:var(--gold-pale);border:1px solid rgba(201,162,79,.4);color:#7A5C12;
  font-size:.8125rem;padding:10px 12px;border-radius:var(--r-sm);margin-bottom:16px}
.sso-tools{margin-top:24px;padding-top:18px;border-top:1px solid var(--border)}
.sso-tools-lab{font:700 10px/1 'Plus Jakarta Sans',sans-serif;letter-spacing:.1em;
  text-transform:uppercase;color:var(--text-muted);text-align:center;margin-bottom:11px}
.sso-tools-row{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.sso-chip{display:flex;align-items:center;justify-content:center;font-size:11.5px;font-weight:600;
  color:var(--text-muted);background:var(--off-white);border:1px solid var(--border);
  border-radius:999px;padding:5px 11px}

/* ─── HUB ──────────────────────────────────────────────── */
.hub-body{background:var(--off-white);min-height:100vh;min-height:100dvh}
.hub-bar{display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:16px 30px;background:var(--white);border-bottom:1px solid var(--border)}
.hub-logo{display:block;width:128px;height:28px;background-color:var(--navy);flex:none;
  -webkit-mask:url('../img/fg-logo.png') no-repeat left center/contain;
          mask:url('../img/fg-logo.png') no-repeat left center/contain}
/* Endorsement lockup: corporate mark, hairline, product name. The rule is a
   gold GRADIENT rather than a flat bar so it reads as a seam between two
   brands instead of a divider chopping the header in half. The FG mark is
   a fine serif; the product name is set in the UI sans at a weight that can
   hold its own beside it without shouting over it. */
.hub-brand{display:flex;align-items:center;gap:13px;min-width:0}
.hub-brand-rule{flex:none;width:1.5px;height:24px;border-radius:1px;
  background:linear-gradient(180deg,rgba(201,162,79,0),var(--gold) 28%,var(--gold) 72%,rgba(201,162,79,0))}
.hub-brand-name{font:700 14.5px/1.2 'Plus Jakarta Sans',sans-serif;color:var(--navy);
  letter-spacing:-.01em;white-space:nowrap}

.hub-user{display:flex;align-items:center;gap:11px}
.hub-avatar{width:34px;height:34px;flex:none;border-radius:50%;background:var(--navy);color:#fff;
  display:grid;place-items:center;font:700 12.5px/1 'Plus Jakarta Sans',sans-serif}
.hub-uname{font-size:13.5px;font-weight:700;color:var(--navy);line-height:1.25}
.hub-urole{font-size:11.5px;color:var(--text-muted);line-height:1.25}
.hub-out{font-size:12.5px;font-weight:600;color:var(--text-muted);text-decoration:none;white-space:nowrap;
  border:1px solid var(--border);border-radius:8px;padding:7px 13px;background:var(--off-white)}
.hub-out:hover{color:var(--navy);border-color:var(--navy)}
.hub-wrap{max-width:1120px;margin:0 auto;padding:38px 30px 56px}
/* --gold (#C9A24F) on the off-white page is 2.26:1 — well under the 4.5:1 AA
   floor for 10.5px text. This deeper gold reads as the same accent and
   measures 4.75:1. Do not swap it back to var(--gold). */
.hub-eyebrow{font:700 10.5px/1 'Plus Jakarta Sans',sans-serif;letter-spacing:.12em;
  text-transform:uppercase;color:#8A6A22;margin-bottom:9px}
.hub-h1{font:800 27px/1.2 'Plus Jakarta Sans',sans-serif;color:var(--navy);margin:0 0 7px}
.hub-lead{font-size:14px;color:var(--text-muted);margin:0 0 30px}
.hub-lead b{color:var(--navy);font-weight:600}
.hub-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(252px,1fr));gap:16px}
.hub-app{position:relative;display:flex;flex-direction:column;text-decoration:none;
  background:var(--white);border:1.5px solid var(--border);border-radius:var(--r-lg);
  padding:22px 20px 18px;transition:all .16s ease;overflow:hidden}
/* A hairline in the tool's own colour. Enough to tell them apart at a glance
   without four saturated cards competing for attention. */
.hub-app::before{content:"";position:absolute;inset:0 0 auto 0;height:3px;
  background:linear-gradient(90deg,var(--ac,var(--navy)),transparent 78%);opacity:.9}
.hub-app--locked::before{opacity:.25;filter:grayscale(1)}

/* Identity row: icon, then the tool name, with the health dot pushed to the
   far end. The name wraps beside the icon rather than under it. */
.hub-top{display:flex;align-items:center;gap:11px;margin-bottom:18px}

/* The figure is the point of the tile — the tool name is its caption. */
/* min-height keeps a text figure ("Published today") on the same baseline
   grid as a numeric one, so the four tiles' names line up across the row. */
.hub-figure{font:800 34px/1 'Plus Jakarta Sans',sans-serif;color:var(--navy);
  letter-spacing:-.02em;margin-bottom:4px;min-height:34px;display:flex;align-items:flex-end}
.hub-figure--text{font-size:20px;line-height:1.15;letter-spacing:-.01em}
.hub-cap{font-size:12px;color:var(--text-muted);margin-bottom:16px;min-height:16px}
.hub-app--locked .hub-figure{color:var(--text-light)}
.hub-app:hover{border-color:var(--navy);transform:translateY(-3px);box-shadow:0 14px 32px rgba(11,31,58,.10)}
.hub-app--here{border-color:var(--navy);box-shadow:0 0 0 3px rgba(11,31,58,.07)}
.hub-icon{width:40px;height:40px;border-radius:11px;display:grid;place-items:center;flex-shrink:0}
.hub-name{font:700 14px/1.25 'Plus Jakarta Sans',sans-serif;color:var(--navy);
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0}
.hub-top .hub-dot{margin-left:auto}
.hub-desc{font-size:12.7px;color:var(--text-muted);line-height:1.5;margin-bottom:14px;min-height:38px}
.hub-foot{margin-top:auto;display:flex;align-items:center;justify-content:space-between;gap:10px;
  padding-top:13px;border-top:1px solid var(--border)}
.hub-stat{font-size:12px;color:var(--text-muted);display:flex;align-items:center;gap:6px}
/* Health dot on each tile. Colour carries the state, but the label beside it
   always says the same thing in words — colour is never the only signal. */
.hub-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;display:inline-block}
.hub-dot--ok{background:var(--success)}
.hub-dot--warn{background:var(--warning)}
.hub-dot--idle{background:var(--text-light)}
.hub-dot--down{background:var(--error);box-shadow:0 0 0 3px rgba(220,38,38,.14)}
.hub-stat{display:flex;align-items:center;gap:6px;min-height:16px}
.hub-warnword{color:var(--error);font-weight:600}

/* Action row: word left, arrow right. On hover the circle fills with the
   tool's own accent and the arrow travels — the whole card is the link, so
   this is a signal of where you are going, not a second button. */
.hub-go{font-size:12.5px;font-weight:700;color:var(--navy);display:flex;align-items:center;
  gap:5px;white-space:nowrap;letter-spacing:.01em}
.hub-go--muted{font-weight:600;color:var(--text-muted)}
.hub-go--down{color:var(--error)}

.hub-arrow{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;
  flex-shrink:0;color:var(--ac,var(--navy));
  border:1.5px solid color-mix(in srgb,var(--ac,var(--navy)) 34%,transparent);
  background:color-mix(in srgb,var(--ac,var(--navy)) 8%,transparent);
  transition:background .18s ease,border-color .18s ease,transform .18s ease,color .18s ease}
.hub-arrow svg{transition:transform .18s ease}
.hub-app:hover .hub-arrow{background:var(--ac,var(--navy));border-color:var(--ac,var(--navy));
  color:#fff;transform:scale(1.06)}
.hub-app:hover .hub-arrow svg{transform:translateX(1.5px)}
/* The lime accent is too light for white glyphs — darken the ink instead. */
.hub-app[style*="#6E8F1E"]:hover .hub-arrow{color:#fff}
@media (prefers-reduced-motion:reduce){
  .hub-arrow,.hub-arrow svg{transition:none}
  .hub-app:hover .hub-arrow{transform:none}
  .hub-app:hover .hub-arrow svg{transform:none}}
.hub-pill{font:700 9.5px/1 'Plus Jakarta Sans',sans-serif;letter-spacing:.08em;text-transform:uppercase;
  padding:4px 7px;border-radius:5px;background:#FEF3C7;color:#92400E}
.hub-pill--here{background:var(--navy);color:#fff}
.hub-app--locked{background:var(--off-white);border-style:dashed;cursor:not-allowed;opacity:.85}
.hub-app--locked:hover{transform:none;box-shadow:none;border-color:var(--border)}
.hub-app--locked .hub-icon{filter:grayscale(1);opacity:.5}
.hub-app--locked .hub-name,.hub-app--locked .hub-go{color:var(--text-muted)}
/* Locked tiles are informational: the tool exists, you just cannot open it.
   The label is de-emphasised further than the name so the card still reads
   as "this is a thing" rather than "this is broken". */
.hub-app--locked .hub-go{font-weight:600;font-size:11.5px;opacity:.8}
.hub-app--locked .hub-stat{display:flex;align-items:center;gap:5px}
.hub-app--locked .hub-desc{opacity:.9}
.hub-notice{margin:0 0 24px;padding:13px 16px;background:var(--gold-pale);
  border:1px solid rgba(201,162,79,.45);border-radius:var(--r-md);font-size:13px;color:#7A5C12}
/* Master-only utility bar. Sits under the grid, separated by a hairline so
   it reads as a footer of tools rather than a sentence about your role. */
.hub-adminbar{display:flex;gap:9px;flex-wrap:wrap;margin-top:30px;padding-top:18px;
  border-top:1px solid var(--border)}
.hub-adminlink{display:inline-flex;align-items:center;gap:7px;text-decoration:none;
  font:600 12.5px/1 'Inter',sans-serif;color:var(--text-muted);background:var(--white);
  border:1px solid var(--border);border-radius:9px;padding:9px 14px;transition:all var(--t)}
.hub-adminlink:hover{color:var(--navy);border-color:var(--navy);
  box-shadow:0 3px 10px rgba(11,31,58,.07);transform:translateY(-1px)}
.hub-adminlink svg{opacity:.75}
.hub-adminlink:hover svg{opacity:1}

/* ─── COLD OPEN ────────────────────────────────────────── */
/* Solid navy sits HERE, on the element that never transforms. On the veil
   it would sweep off-viewport as the veil rotates, and the page would show
   through at the corners — the same trap the Sentinel cold open documents. */
.fpg-co{position:fixed;inset:0;z-index:9999;display:none;pointer-events:none;
  overflow:hidden;background:#08172B}
.fpg-co.is-playing{display:block;animation:fpg-co-clear 950ms ease-out 850ms both}
.fpg-co__veil{position:absolute;inset:-30%;
  background-image:
    radial-gradient(40% 40% at 24% 32%,rgba(201,162,79,.80) 0%,transparent 64%),
    radial-gradient(38% 38% at 76% 68%,rgba(27,69,112,.95) 0%,transparent 64%),
    radial-gradient(30% 30% at 62% 20%,rgba(201,162,79,.35) 0%,transparent 62%),
    radial-gradient(70% 60% at 50% 50%,rgba(255,255,255,.10) 0%,transparent 60%);
  filter:blur(30px);will-change:transform,opacity}
.fpg-co.is-playing .fpg-co__veil{animation:fpg-co-drift 2600ms ease-in-out infinite}
@keyframes fpg-co-drift{
  0%{transform:translate(0,0) rotate(0deg) scale(1)}
  50%{transform:translate(5%,-4%) rotate(140deg) scale(1.16)}
  100%{transform:translate(0,0) rotate(360deg) scale(1)}}
@keyframes fpg-co-clear{from{opacity:1}to{opacity:0}}
.fpg-co__mark{position:absolute;inset:0;display:grid;place-items:center;opacity:0}
.fpg-co.is-playing .fpg-co__mark{animation:fpg-co-mark 1800ms cubic-bezier(.22,.9,.16,1) both}
@keyframes fpg-co-mark{
  0%{opacity:0;transform:scale(1.28)}
  20%{opacity:1;transform:scale(1)}
  62%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(.82)}}
.fpg-co__logo{width:min(38vw,340px);height:76px;
  -webkit-mask:url('../img/fg-logo.png') no-repeat center/contain;
          mask:url('../img/fg-logo.png') no-repeat center/contain;
  background:linear-gradient(105deg,#F4F7FB 0%,#F4F7FB 36%,#E7C87A 47%,#FFF6DF 51%,
             #E7C87A 55%,#F4F7FB 66%,#F4F7FB 100%);
  background-size:280% 100%;background-position:120% 0}
.fpg-co.is-playing .fpg-co__logo{animation:fpg-co-shine 1150ms ease-out 200ms both}
@keyframes fpg-co-shine{from{background-position:120% 0}to{background-position:-40% 0}}
.fpg-co__rule{position:absolute;left:50%;top:calc(50% + 62px);width:0;height:1px;
  background:linear-gradient(90deg,transparent,rgba(201,162,79,.9),transparent);
  transform:translateX(-50%);opacity:0}
.fpg-co.is-playing .fpg-co__rule{animation:fpg-co-rule 1300ms cubic-bezier(.22,.9,.16,1) 320ms both}
@keyframes fpg-co-rule{
  0%{width:0;opacity:0}
  40%{width:min(30vw,280px);opacity:1}
  100%{width:min(30vw,280px);opacity:0}}
.fpg-entering{animation:fpg-co-settle 950ms cubic-bezier(.22,.9,.16,1) 850ms both;
  transform-origin:50% 42%}
@keyframes fpg-co-settle{
  from{transform:scale(1.055);filter:blur(12px);opacity:0}
  55%{opacity:1}
  to{transform:scale(1);filter:blur(0);opacity:1}}
@media (prefers-reduced-motion:reduce){
  .fpg-co,.fpg-co.is-playing{display:none!important}
  .fpg-entering{animation:none!important}}
@media print{.fpg-co{display:none!important}.fpg-entering{animation:none!important}}

/* ─── ACCESS ADMIN ─────────────────────────────────────── */
.acc-table{width:100%;border-collapse:collapse;background:var(--white);
  border:1px solid var(--border);border-radius:var(--r-md);overflow:hidden}
.acc-table th{background:var(--off-white);font:700 11px/1 'Plus Jakarta Sans',sans-serif;
  letter-spacing:.06em;text-transform:uppercase;color:var(--text-muted);
  padding:12px 14px;text-align:left;border-bottom:1px solid var(--border)}
.acc-table td{padding:12px 14px;border-bottom:1px solid var(--border);font-size:13px;vertical-align:middle}
.acc-table tr:last-child td{border-bottom:none}
.acc-who{font-weight:700;color:var(--navy)}
.acc-mail{font-size:11.5px;color:var(--text-muted)}
.acc-chk{width:17px;height:17px;accent-color:var(--navy);cursor:pointer}
.acc-all{font-size:11px;color:var(--text-muted);font-style:italic}
.acc-off{opacity:.5}

/* ─── ACTIVITY ─────────────────────────────────────────── */
.act-kpis{display:flex;gap:14px;flex-wrap:wrap;margin-bottom:22px}
.act-kpi{background:var(--white);border:1.5px solid var(--border);border-radius:var(--r-md);
  padding:14px 20px;min-width:120px}
.act-kpi--warn{border-color:rgba(217,119,6,.45);background:#FFFBEB}
.act-kpi-n{font:800 24px/1 'Plus Jakarta Sans',sans-serif;color:var(--navy)}
.act-kpi-l{font-size:11.5px;color:var(--text-muted);margin-top:5px}
.act-filters{display:flex;align-items:center;gap:7px;flex-wrap:wrap;margin-bottom:18px}
.act-chip{font-size:12px;font-weight:600;color:var(--text-muted);text-decoration:none;
  background:var(--white);border:1px solid var(--border);border-radius:999px;padding:6px 13px}
.act-chip:hover{color:var(--navy);border-color:var(--navy)}
.act-chip--on{background:var(--navy);color:#fff;border-color:var(--navy)}
.act-sep{width:1px;height:18px;background:var(--border);margin:0 4px}
.act-when{color:var(--text-muted);font-size:12px;white-space:nowrap}
.act-empty{color:var(--text-muted);font-size:13.5px;background:var(--white);
  border:1px dashed var(--border);border-radius:var(--r-md);padding:26px;text-align:center}
.act-foot{font-size:11.5px;color:var(--text-muted);margin-top:12px}


/* ═══ RESPONSIVE + TOUCH ═══════════════════════════════════
   Everything below was added after a device audit. Two separate concerns,
   deliberately keyed off different queries:

     @media (max-width:560px)  — LAYOUT. What fits on a narrow screen.
     @media (pointer:coarse)   — TOUCH. Finger-sized hit areas.

   They are not the same question. A 13" laptop is narrow-ish but has a
   mouse; an iPad is wide but has fingers. Keying the 44px targets off
   pointer type means the desktop keeps its compact, precise controls and
   every touch device gets targets you can actually hit. */

/* ─── Safe areas (notch, rounded corners, home indicator) ─── */
.auth-body{
  padding:calc(24px + env(safe-area-inset-top)) max(24px,env(safe-area-inset-right))
          calc(24px + env(safe-area-inset-bottom)) max(24px,env(safe-area-inset-left))}
.hub-bar{
  padding-left:max(30px,env(safe-area-inset-left));
  padding-right:max(30px,env(safe-area-inset-right))}
.hub-wrap{padding-bottom:calc(56px + env(safe-area-inset-bottom))}

/* ─── Keyboard focus ───────────────────────────────────────
   There were 13 :hover rules and one :focus-visible, so a keyboard user had
   almost nothing to follow. :focus-visible (not :focus) keeps the ring off
   mouse and touch users. */
a:focus-visible,button:focus-visible,input:focus-visible,
summary:focus-visible,[tabindex]:focus-visible{
  outline:2px solid var(--gold);outline-offset:2px;border-radius:5px}
.hub-tile:focus-visible{outline:2px solid var(--gold);outline-offset:3px}

/* Default iOS/Android tap flash is a grey box that ignores our radii. */
a,button,label,input,summary{-webkit-tap-highlight-color:rgba(11,31,58,.10)}

/* ─── LAYOUT: narrow screens ─────────────────────────────── */
@media (max-width:560px){
  /* The product name and its seam go first — the mark plus the page heading
     underneath already say where you are. */
  .hub-brand-rule,.hub-brand-name{display:none}
  .hub-bar{padding-top:12px;padding-bottom:12px;gap:10px}
  .hub-logo{width:106px;height:24px}
  /* The name/role block was forcing the bar over 390px, which shoved the
     avatar underneath the logo. Identity is still on screen: the initials
     stay, and the greeting below says the full name. */
  .hub-uname,.hub-urole{display:none}
  .hub-wrap{padding-left:20px;padding-right:20px;padding-top:26px}
  .hub-h1{font-size:23px}
  .act-kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:9px}
  .act-kpi{padding:13px 9px;min-width:0}
  .act-kpi-n{font-size:20px}
  .act-kpi-l{font-size:10.5px}
}

/* ─── TOUCH: finger-sized targets ────────────────────────── */
@media (pointer:coarse){
  .hub-out,.hub-adminlink,.act-chip{
    min-height:44px;display:inline-flex;align-items:center;justify-content:center}
  .auth-pw-toggle{width:44px;height:44px;right:3px}
  .auth-btn{padding:15px}
  /* 17px checkboxes on a permissions grid is a mis-tap that silently grants
     or revokes a tool. The box grows, and the whole cell becomes the target. */
  .acc-chk{width:24px;height:24px}
  .acc-table td{padding-top:6px;padding-bottom:6px}
  .acc-cell{padding:0!important}
  .acc-cell label{display:flex;align-items:center;justify-content:center;
    min-height:48px;width:100%;cursor:pointer}
}

/* ─── The access grid must stay reachable on a phone ───────
   The table's natural width is ~510px. Below that it was simply clipped:
   the Website Admin and Payables columns could not be reached at all, so a
   master admin could not grant or revoke them from a phone. It now scrolls
   inside its own rail, with the person's name pinned so you always know
   which row you are toggling. */
.acc-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;
  border-radius:var(--r-md);border:1px solid var(--border);background:var(--white)}
/* border-collapse:collapse breaks position:sticky on table cells in Chrome and
   Safari — the name column just scrolled away with the rest, which is the one
   thing the sticky column exists to prevent. separate + border-spacing:0 looks
   identical and lets sticky work, so the row borders are drawn per-cell. */
.acc-scroll .acc-table{border:none;border-radius:0;min-width:520px;margin:0;
  border-collapse:separate;border-spacing:0;
  /* .acc-table carries overflow:hidden to clip its own rounded corners. Any
     non-visible overflow between a sticky cell and its scrollport makes that
     ancestor the cell's scroll container, so the name column never stuck. The
     rail does the corner clipping now, so the table gives it up. */
  overflow:visible}
.acc-scroll .acc-table td,.acc-scroll .acc-table th{border-bottom:1px solid var(--border)}
.acc-scroll .acc-table tr:last-child td{border-bottom:none}
.acc-scroll th:first-child,.acc-scroll td:first-child{
  position:sticky;left:0;z-index:1;background:var(--white);
  box-shadow:1px 0 0 var(--border)}
.acc-scroll thead th:first-child{background:var(--off-white);z-index:3}
.acc-scroll thead th{position:sticky;top:0;z-index:2;background:var(--off-white)}
.acc-hint{display:none;font-size:11.5px;color:var(--text-muted);margin:9px 0 0}
@media (max-width:700px){.acc-hint{display:block}}


/* ═══ HEADSHOTS ════════════════════════════════════════════
   Photos come from the Support Center's team records through avatar.php.
   Anyone without one keeps the initials, so both states have to look
   deliberate rather than one looking like a failed image. */
.hub-avatar,.acc-face{overflow:hidden;position:relative}
.ops-av-ini{display:block;line-height:1}
/* Layered over the initials, so the circle is never an empty disc while a
   lazy image is pending — and stays readable if the file is gone. */
.ops-avatar-img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block;background:var(--navy)}

/* Access grid: face, then name and address. */
.acc-person{display:flex;align-items:center;gap:11px;min-width:0}
.acc-face{flex:none;width:34px;height:34px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;background:var(--navy);color:#fff;
  font:700 12px/1 'Plus Jakarta Sans',sans-serif}
.acc-off .acc-face{filter:grayscale(1)}
@media (max-width:560px){
  .acc-face{width:30px;height:30px;font-size:11px}
  .acc-person{gap:9px}
}


/* ─── Activity: who did it ────────────────────────────────
   Name over address, with the face alongside. An address alone made every
   row look alike; a face is the fastest thing to scan a log by. */
.act-who{display:flex;flex-direction:column;gap:1px;min-width:0}
.act-who .acc-who{font-size:13px;line-height:1.25}
.act-who .acc-mail{line-height:1.25;overflow:hidden;text-overflow:ellipsis}
/* An address we cannot match to an admin — refused sign-ins live here, so
   "unrecognised" should look unrecognised rather than borrow someone's
   initials. */
.acc-face--unknown{background:var(--off-white);color:var(--text-muted);
  border:1px dashed var(--border);font-weight:800}
/* The activity table only needs the rail on narrow screens; at desktop width
   it fits, and a min-width would leave it stranded short of the container. */
.acc-scroll--act .acc-table{min-width:0}
@media (max-width:620px){
  .acc-scroll--act .acc-table{min-width:430px}
  /* Name identifies the person, so the address can go — but only where
     there IS a name. Unmatched rows keep it. */
  .act-who--named .act-who-mail{display:none}
}
