/* =========================================================================
   Xe4 Photon - design system
   Globals, keyframes and hover behaviours extracted verbatim from the
   "Xe4 Photon" Claude Design handoff. Layout/visual styling stays inline on
   the elements (faithful to the prototype); only things that cannot live in an
   inline style attribute - keyframes, ::selection, scrollbars and :hover
   states - are expressed here as classes.
   ========================================================================= */

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body { background: #0a0b0e; }
::selection { background: #FFB23C; color: #0a0b0e; }

/* ---- keyframes (from the prototype <style> block) ---- */
@keyframes radar-sweep { to { transform: rotate(360deg); } }
@keyframes orbit-cw    { to { transform: rotate(360deg); } }
@keyframes orbit-ccw   { to { transform: rotate(-360deg); } }
@keyframes pulse-risk  { 0%,100% { transform: translate(-50%,-50%) scale(1); opacity:1; } 50% { transform: translate(-50%,-50%) scale(1.6); opacity:.45; } }
@keyframes ring-pulse  { 0% { transform: translate(-50%,-50%) scale(.5); opacity:.9; } 100% { transform: translate(-50%,-50%) scale(2.6); opacity:0; } }
@keyframes blink       { 0%,100% { opacity:1; } 50% { opacity:.2; } }
@keyframes float-y     { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }
@keyframes reveal-up   { from { opacity:0; transform: translateY(26px); } to { opacity:1; transform: translateY(0); } }
/* home "loop" dashed line: one 14px dash-period is 14px, so a 140px slide
   wraps seamlessly; transform keeps it compositor-only (the old dash-move
   animated background-position, repainting the strip every frame). 140px/1s
   matches the old 300%/18s speed. */
@keyframes dash-slide  { to { transform: translateX(140px); } }

/* ---- scrollbars ---- */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: #0a0b0e; }
::-webkit-scrollbar-thumb { background: #23262e; border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: #33373f; }

/* =========================================================================
   Hero backdrop performance.
   The backdrop's MOVING parts (spinning meridians, orbiting satellites) are
   drawn by app.js (initHeroBackdrop) on the single
   <canvas id="hero-backdrop-canvas"> at ~30fps. They used to be CSS
   transform animations on huge DOM elements - five 1500px meridian rings
   inside a preserve-3d context plus two 1640/1760px satellite rings - which
   kept ~8 viewport-sized, mostly-transparent compositor layers alive and
   re-blended every one of them across the screen on every frame, forever.
   That GPU blend bandwidth (not main-thread JS) is what made the page lag,
   especially on integrated GPUs at DPR > 1; pausing offscreen, flattening
   the orbit planes to scaleX and removing the nav backdrop-filter had
   already been tried and were not enough. Only static scenery (limb,
   latitude rings, orbit outlines, glow, stars, fades) stays in the DOM,
   painted once and never invalidated.

   The canvas loop pauses via IntersectionObserver when the backdrop scrolls
   out of view. It is deliberately NOT gated on prefers-reduced-motion:
   Windows reports "reduce" whenever OS animation effects are off, and the
   globe is the hero's centrepiece - it is wanted in motion (a previous
   reduced-motion rule froze it and was reverted). `will-change` also remains
   deliberately absent everywhere: pinning multi-MB DPR-scaled textures made
   things worse historically.
   ========================================================================= */

/* =========================================================================
   Hover states (the prototype's style-hover="..." attributes).
   Base styling remains inline on each element; these rules only add the
   :hover delta so the transitions defined inline animate into them.
   ========================================================================= */

/* nav text links */
.hv-nav-link:hover { color: #F3F1EC; background: rgba(255,255,255,0.04); }

/* small gradient CTA in the nav bar */
.hv-cta-sm:hover { box-shadow: 0 8px 30px rgba(255,150,40,0.45); transform: translateY(-2px); }

/* hero gradient CTA */
.hv-cta-hero:hover { box-shadow: 0 10px 38px rgba(255,150,40,0.5); transform: translateY(-2px); }

/* CTA-band gradient CTA */
.hv-cta-band:hover { box-shadow: 0 10px 40px rgba(255,150,40,0.55); transform: translateY(-2px); }

/* ghost (light/translucent) secondary button - hero variant */
.hv-ghost:hover { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.22); transform: translateY(-2px); }

/* ghost secondary button - CTA-band variant (slightly brighter fill) */
.hv-ghost-2:hover { background: rgba(255,255,255,0.09); border-color: rgba(255,255,255,0.22); transform: translateY(-2px); }

/* amber outlined ghost button ("Explore the product →") */
.hv-amber-ghost:hover { background: rgba(255,178,60,0.08); border-color: rgba(255,178,60,0.55); transform: translateY(-2px); }

/* feature cards */
.hv-feature-card:hover { border-color: rgba(255,178,60,0.45); background: #16181e; transform: translateY(-6px); box-shadow: 0 20px 44px rgba(0,0,0,0.4); }

/* "how it works" numbered step badge */
.hv-step-num:hover { transform: scale(1.12); box-shadow: 0 0 26px rgba(255,178,60,0.45); background: #16181c; }

/* pricing - standard plan card */
.hv-price-card:hover { transform: translateY(-8px); border-color: rgba(255,178,60,0.4); box-shadow: 0 28px 60px rgba(0,0,0,0.45); }

/* pricing - featured (popular) plan card */
.hv-price-card-featured:hover { transform: translateY(-8px); box-shadow: 0 34px 78px rgba(255,150,40,0.24); }

/* pricing - light/translucent plan button ("Start free trial", "Talk to us") */
.hv-btn-light:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,178,60,0.4); transform: translateY(-2px); }

/* FAQ accordion header */
.hv-faq:hover { background: rgba(255,255,255,0.03); }

/* footer text links */
.hv-footer-link:hover { color: #F3F1EC; }

/* =========================================================================
   Forms (request-access / contact). Inline base styling sits on the elements;
   :focus and ::placeholder can't be inline, so they live here.
   ========================================================================= */
.field {
  width: 100%;
  background: #0e1014;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
  padding: 12px 14px;
  color: #F3F1EC;
  font-family: 'Manrope', sans-serif;
  font-size: 14px;
  transition: border-color .2s ease, box-shadow .2s ease, background .2s ease;
}
.field::placeholder { color: #62666E; }
.field:focus {
  outline: none;
  border-color: rgba(255,178,60,0.6);
  box-shadow: 0 0 0 3px rgba(255,178,60,0.12);
  background-color: #101216;
}
select.field {
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  padding-right: 38px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%239CA0A8' stroke-width='1.6' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
textarea.field { resize: vertical; min-height: 120px; line-height: 1.55; }

/* submit buttons reuse the gradient-CTA hover feel */
.hv-submit:hover { box-shadow: 0 10px 38px rgba(255,150,40,0.5); transform: translateY(-2px); }

/* admin / operations console table rows */
.hv-admin-row:hover { background: rgba(255,255,255,0.025); }
/* admin sidebar items */
.hv-admin-nav:hover { background: rgba(255,255,255,0.04); color: #F3F1EC; }

/* =========================================================================
   Account pages (/account/*). Unlike the marketing pages these are partly
   rendered by JS (account.js), so the shared look lives in classes rather
   than inline styles - and they must be mobile-responsive (public site).
   ========================================================================= */
.acct-wrap { max-width: 1100px; margin: 0 auto; padding: 64px 32px 100px; }
.acct-auth-card, .acct-card {
  background: #121419;
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 18px;
  padding: 30px;
  min-width: 0;
}
.acct-auth-card { max-width: 440px; margin: 0 auto; }
.acct-grid { display: grid; grid-template-columns: 1.35fr 1fr; gap: 24px; align-items: start; }
.acct-col { display: flex; flex-direction: column; gap: 24px; min-width: 0; }
.acct-kicker {
  font-family: 'JetBrains Mono', monospace;
  font-size: 11px; letter-spacing: 1.5px; color: #FFB23C; margin-bottom: 18px;
}
.acct-label {
  display: block; font-family: 'JetBrains Mono', monospace;
  font-size: 11px; letter-spacing: 1px; color: #9CA0A8; margin-bottom: 8px;
}
.acct-muted { color: #9CA0A8; }
.acct-link { color: #FFB23C; text-decoration: none; }
.acct-link:hover { text-decoration: underline; }

/* inline form feedback (hidden until JS fills them) */
.acct-error, .acct-ok, .acct-note {
  display: none; font-size: 13px; line-height: 1.55;
  border-radius: 10px; padding: 10px 14px; margin-bottom: 18px;
}
.acct-error { color: #ff9d8a; background: rgba(255,110,80,0.08); border: 1px solid rgba(255,110,80,0.28); }
.acct-ok    { color: #57d9a3; background: rgba(87,217,163,0.08); border: 1px solid rgba(87,217,163,0.28); }
.acct-note  { color: #C7C9CE; background: rgba(255,178,60,0.07); border: 1px solid rgba(255,178,60,0.25); }

/* license status pill */
.acct-pill {
  display: inline-flex; align-items: center; gap: 7px;
  font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 1px;
  padding: 5px 12px; border-radius: 999px; border: 1px solid rgba(255,255,255,0.15);
  color: #C7C9CE;
}
.acct-pill::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; box-shadow: 0 0 8px currentColor; }
.acct-pill.ok   { color: #57d9a3; border-color: rgba(87,217,163,0.35); background: rgba(87,217,163,0.07); }
.acct-pill.warn { color: #FFB23C; border-color: rgba(255,178,60,0.35);  background: rgba(255,178,60,0.07); }
.acct-pill.bad  { color: #ff9d8a; border-color: rgba(255,110,80,0.35);  background: rgba(255,110,80,0.07); }

/* secondary + danger buttons (the primary gradient button reuses .hv-submit) */
.acct-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: 'Manrope', sans-serif; font-size: 14px; font-weight: 500;
  color: #F3F1EC; background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.12); border-radius: 10px;
  padding: 11px 18px; cursor: pointer; text-decoration: none;
  transition: transform .22s ease, background .22s ease, border-color .22s ease;
}
.acct-btn:hover { background: rgba(255,255,255,0.08); border-color: rgba(255,255,255,0.22); transform: translateY(-1px); }
.acct-btn:disabled, .hv-submit:disabled { opacity: .55; cursor: default; transform: none; }
.acct-danger-btn {
  display: inline-flex; align-items: center; justify-content: center;
  font-family: 'Manrope', sans-serif; font-size: 14px; font-weight: 600;
  color: #ff9d8a; background: rgba(255,110,80,0.06);
  border: 1px solid rgba(255,110,80,0.32); border-radius: 10px;
  padding: 11px 18px; cursor: pointer;
  transition: background .22s ease, border-color .22s ease;
}
.acct-danger-btn:hover { background: rgba(255,110,80,0.13); border-color: rgba(255,110,80,0.5); }
.acct-danger-btn:disabled { opacity: .55; cursor: default; }

/* device rows (built by account.js with textContent - no markup injection) */
.acct-device {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 14px 2px; border-top: 1px solid rgba(255,255,255,0.06);
}
.acct-device:first-child { border-top: none; }
.acct-device-name { font-size: 14px; font-weight: 600; color: #F3F1EC; overflow-wrap: anywhere; }
.acct-device-meta { font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: .5px; color: #62666E; margin-top: 3px; }
.acct-revoke {
  flex-shrink: 0; font-family: 'Manrope', sans-serif; font-size: 13px; font-weight: 500;
  color: #ff9d8a; background: transparent; border: 1px solid rgba(255,110,80,0.3);
  border-radius: 8px; padding: 7px 13px; cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.acct-revoke:hover { background: rgba(255,110,80,0.1); border-color: rgba(255,110,80,0.5); }
.acct-revoke:disabled { opacity: .5; cursor: default; }

/* =========================================================================
   Admin Panel (/admin/*). Staff-only, JS-rendered like the account pages -
   reuses the .acct-* kit above and adds only what the panel needs.
   ========================================================================= */
.adm-nav {
  display: block; width: 100%; text-align: left;
  font-family: 'Manrope', sans-serif; font-size: 13px; color: #9CA0A8;
  background: transparent; border: none; border-radius: 8px;
  padding: 9px 12px; margin-bottom: 3px; cursor: pointer;
  transition: background .2s ease, color .2s ease;
}
.adm-nav.active { background: rgba(255,178,60,0.1); color: #F3F1EC; }

/* label/value rows on the account-detail license card */
.adm-kv {
  display: flex; align-items: baseline; justify-content: space-between; gap: 16px;
  padding: 8px 0; border-bottom: 1px solid rgba(255,255,255,0.05);
  font-size: 13px;
}
.adm-kv > span { color: #62666E; font-family: 'JetBrains Mono', monospace; font-size: 11px; letter-spacing: 1px; flex-shrink: 0; }
.adm-kv > b { font-weight: 600; color: #F3F1EC; text-align: right; overflow-wrap: anywhere; min-width: 0; }

/* data tables built by admin.js (textContent only) */
.adm-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.adm-table th {
  text-align: left; font-family: 'JetBrains Mono', monospace; font-weight: 500;
  font-size: 10px; letter-spacing: 1px; color: #62666E;
  padding: 0 8px 10px; border-bottom: 1px solid rgba(255,255,255,0.06);
  white-space: nowrap;
}
.adm-table td {
  padding: 11px 8px; border-bottom: 1px solid rgba(255,255,255,0.04);
  color: #C7C9CE; vertical-align: top; overflow-wrap: anywhere;
}
.adm-table tr:last-child td { border-bottom: none; }
.adm-table tbody tr.adm-row-click { cursor: pointer; }
.adm-table tbody tr.adm-row-click:hover { background: rgba(255,255,255,0.025); }
.adm-mono { font-family: 'JetBrains Mono', monospace; font-size: 11px; color: #9CA0A8; }

/* =========================================================================
   Photon app facsimile (.pv-*) - the home page's "console" preview is a
   faithful static mock of the REAL desktop app, not an invented web console:
   photon/python/theme.py DARK palette, ui_main.py shell, and the
   constellation_viewer.py Viewer tab.

   Retempered for Interface Direction C ("Console"), the design the app
   shipped in v0.30. What that means here, and why the rules below look
   austere:

     1. TWO SURFACES. Panel and raised. Elevation is a tone step, never a
        shadow and never a border-radius. Hence zero radius on everything
        inside the window - the only rounded thing left is the browser's own
        idea of the page.
     2. MONO FOR DATA. The one-question test: could this string change on the
        next propagation tick? Then it is a value, and values are mono at
        weight 400-600 - never bold, never tracked (digits must align down a
        column). Labels are uppercase mono, tracked +0.6px.
     3. AMBER IS A BUDGET. Exactly one filled amber control per screen (here:
        PLAY), one amber playhead, and as many 2px amber rules as the layout
        needs. Nothing else may be amber.
     4. BORDERS ARE STRUCTURAL. Solid #RRGGBB, no alpha - an rgba() hairline
        over two different surfaces resolves to two different colours, which
        is exactly the kind of drift this palette exists to prevent.

   Colour tokens are copied 1:1 from theme.py's DARK palette; the metric
   constants (28px bar, 40px alert row, 3px meter, 6px square, 2px edge) are
   theme.py's too. Everything is static - no animations - so it paints once
   and costs nothing.
   ========================================================================= */
.pv-window {
  /* theme.py DARK - surfaces */
  --pv-deep: #08090B; --pv-base: #0B0D10; --pv-panel: #101317;
  --pv-head: #161A1F; --pv-raised: #1B2027; --pv-ink: #090A0D;
  --pv-rule: #14181D; --pv-view: #070A0D;
  /* accent: FILL is what gets painted, RULE is what marks "current". Dark
     binds both to amber; the app's LIGHT palette binds RULE to ink instead,
     which is why they are two tokens and not one. */
  --pv-accent: #E0A93F; --pv-accent-rule: #E0A93F; --pv-on-accent: #1A1408;
  /* text */
  --pv-text: #DDE3E8; --pv-mute: #9AA4AE; --pv-dim: #7B848D;
  /* structure - solid, no alpha */
  --pv-border: #1E242B;
  /* severity + category channels */
  --pv-good: #5FBF8F; --pv-amber: #D89B3C; --pv-rust: #E85D52;
  --pv-info-edge: #232A32; --pv-info-square: #3A424B;
  --pv-fleet: #5FBF8F; --pv-payload: #55A8CC; --pv-unknown: #7B848D;
  --pv-land: #636872;
  font-family: 'IBM Plex Sans', 'Segoe UI', sans-serif;
  font-size: 13px; color: var(--pv-text);
  background: var(--pv-base);
  border: 1px solid var(--pv-border); border-radius: 0;
  overflow: hidden; text-align: left;
}
/* The app's MONO_FAMILY. JetBrains Mono is the page's already-loaded
   fallback, so the mock still sets in a monospace face if Plex is slow. */
.pv-mono { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace; }
/* Uppercase caption. theme.track_label(+0.6px) - the one place tracking is
   correct, because it is a label and not a value. */
.pv-cap { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 10px; font-weight: 600; letter-spacing: 0.6px;
  color: var(--pv-mute); text-transform: uppercase; }
/* Rule 01, enforced rather than trusted: nothing inside the window is round.
   The three things that legitimately are - the Earth, the selected orbit, and
   the _OrbMark glyph - carry an inline border-radius:50%, and an inline style
   outranks this. So new markup is square by default and roundness has to be
   asked for on purpose. */
.pv-window * { border-radius: 0; }

/* OS title bar + app header + primary tab strip (ui_main._build_header).
   HEADER_H = 40. */
.pv-titlebar { display: flex; align-items: center; gap: 10px; height: 28px;
  padding: 0 0 0 10px; background: var(--pv-deep);
  border-bottom: 1px solid var(--pv-border);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace; }
.pv-titlebar-title { font-size: 11px; color: var(--pv-dim); }
.pv-winbtn { width: 40px; height: 28px; display: flex; align-items: center;
  justify-content: center; color: var(--pv-mute); font-size: 11px; }
.pv-header { display: flex; align-items: center; gap: 10px; height: 40px;
  padding: 0 10px; background: var(--pv-deep);
  border-bottom: 1px solid var(--pv-border); }
.pv-logo { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 13px; font-weight: 600; letter-spacing: 1.4px; }
.pv-menu { display: flex; gap: 2px; }
.pv-menu span { color: var(--pv-mute); font-size: 12px; padding: 4px 10px;
  letter-spacing: 0.4px; }
/* SearchField: 260x28, sunk to INK. */
.pv-search { width: 260px; height: 28px; margin-left: auto; display: flex;
  align-items: center; gap: 8px; background: var(--pv-ink);
  border: 1px solid var(--pv-border); padding: 0 9px;
  color: var(--pv-dim); font-size: 11px; white-space: nowrap; overflow: hidden;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace; }
/* Segmented: no pill, no fill - the current item is a 2px rule under it. */
.pv-seg { display: flex; height: 28px; background: var(--pv-panel);
  border: 1px solid var(--pv-border); }
.pv-seg span { display: flex; align-items: center; padding: 0 12px;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; font-weight: 600; color: var(--pv-mute);
  border-bottom: 2px solid transparent; }
.pv-seg .on { background: var(--pv-raised); color: var(--pv-text);
  border-bottom-color: var(--pv-accent-rule); }
.pv-iconbtn { width: 28px; height: 28px; display: flex; align-items: center;
  justify-content: center; background: var(--pv-panel);
  border: 1px solid var(--pv-border); color: var(--pv-mute); flex-shrink: 0; }
/* overflow-x: the strip scrolls rather than bursting the (full-width) card
   once the body grid collapses to one column. */
.pv-tabs { display: flex; padding: 0 8px; background: var(--pv-deep);
  border-bottom: 1px solid var(--pv-border);
  overflow-x: auto; scrollbar-width: none; }
.pv-tabs::-webkit-scrollbar { display: none; }
.pv-tab { padding: 8px 16px; font-size: 12px; font-weight: 600;
  color: var(--pv-mute); letter-spacing: 0.4px;
  border-bottom: 2px solid transparent; white-space: nowrap; }
.pv-tab.on { color: var(--pv-text); border-bottom-color: var(--pv-accent-rule); }

/* body: left rail (RAIL_L 288) | viewport | right rail (RAIL_R 330).
   GUTTER = 8, BODY_PAD = 8. */
.pv-body { display: grid; grid-template-columns: 288px 1fr 330px; gap: 8px;
  padding: 8px; }

/* Panel + PanelBar - the load-bearing idea. A panel is a bordered box on the
   PANEL tone with a BAR_H(28) header bar one tone step up (PANEL_HEAD). The
   bar carries the tracked caption and an optional right-hand count; the body
   gets PANEL_PAD(10). No radius, no shadow: the tone step IS the elevation. */
.pv-panel { background: var(--pv-panel); border: 1px solid var(--pv-border);
  min-width: 0; display: flex; flex-direction: column; }
.pv-bar { display: flex; align-items: center; gap: 8px; height: 28px;
  padding: 0 10px; background: var(--pv-head);
  border-bottom: 1px solid var(--pv-border); flex-shrink: 0; }
.pv-bar .pv-count { margin-left: auto;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; font-weight: 600; color: var(--pv-text); }
.pv-pad { padding: 10px; min-width: 0; }
.pv-hint { font-size: 11px; line-height: 1.45; color: var(--pv-dim); }

/* StatusChip: a 6px filled square plus coloured mono text. No tint fill, no
   border, no radius - the tint was alpha, which rule 04 bans. */
.pv-chip { display: inline-flex; align-items: center; gap: 6px; height: 28px;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; font-weight: 600; color: var(--sq, var(--pv-mute)); }
.pv-chip::before { content: ''; width: 6px; height: 6px;
  background: var(--sq, var(--pv-mute)); flex-shrink: 0; }

/* AlertRow: ROW_ALERT(40), a 2px severity edge and a 6px severity square -
   severity encoded TWICE in one hue, so it survives both a mono display and
   a colour-blind reader. Mono 12/600 title over a sans 11 subline; the metric
   splits its unit off into --pv-mute so the number keeps the column. */
.pv-alert { position: relative; display: flex; align-items: center; gap: 9px;
  height: 40px; padding: 0 10px 0 12px;
  border-bottom: 1px solid var(--pv-rule); }
.pv-alert:last-child { border-bottom: none; }
.pv-alert.on { background: var(--pv-raised); }
.pv-alert::before { content: ''; position: absolute; left: 0; top: 0;
  bottom: 0; width: 2px; background: var(--sev); }
.pv-alert i { width: 6px; height: 6px; background: var(--sev); flex-shrink: 0; }
.pv-alert .pv-a-name { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 12px; font-weight: 600; color: var(--pv-text); }
.pv-alert .pv-a-sub { font-size: 11px; color: var(--pv-mute); }
.pv-alert .pv-a-metric { margin-left: auto;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 12px; font-weight: 600; color: var(--pv-text);
  white-space: nowrap; }
.pv-alert .pv-a-metric u { text-decoration: none; font-weight: 400;
  color: var(--pv-mute); margin-left: 2px; }

/* DonutKPI, retempered: the ring is gone. A KPI is now a label, a mono value
   and a METER_H(3) bar - 34px tall instead of 156. LABEL_W = 82. */
.pv-kpi { display: flex; align-items: baseline; gap: 8px; margin-bottom: 4px; }
.pv-kpi .pv-cap { width: 82px; flex-shrink: 0; }
.pv-kpi .pv-val { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 15px; font-weight: 600; color: var(--pv-text); }
.pv-kpi .pv-unit { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--pv-mute); }
.pv-kpi .pv-delta { margin-left: auto;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--pv-mute); }
.pv-meter { height: 3px; background: var(--pv-ink); margin-bottom: 12px; }
.pv-meter span { display: block; height: 3px; background: var(--col); }

/* Value cells (#RowCard): sunk to INK, mono value, tracked caption. */
.pv-cell { background: var(--pv-ink); border: 1px solid var(--pv-border);
  padding: 5px 8px 6px; min-width: 0; }
.pv-cell b { display: block;
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 9px; font-weight: 600; letter-spacing: 0.6px;
  color: var(--pv-mute); margin-bottom: 2px; }
.pv-cell span { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 12px; font-weight: 600; color: var(--pv-text); }
.pv-cell small { font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 10px; color: var(--pv-mute); margin-left: 3px; }

/* Buttons, ROW_CONTROL(28). Secondary is a bordered box on PANEL; #Accent is
   the ONE filled amber control on the screen; LIVE is green text over a green
   2px rule (a state, not a primary action). */
.pv-btn { display: inline-flex; align-items: center; justify-content: center;
  height: 28px; padding: 0 11px; background: var(--pv-panel);
  border: 1px solid var(--pv-border);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; font-weight: 600; color: var(--pv-text);
  white-space: nowrap; }
.pv-btn.accent { background: var(--pv-accent); border-color: var(--pv-accent);
  color: var(--pv-on-accent); }
.pv-btn.ghost { background: transparent; color: var(--pv-mute); }
.pv-btn.live { background: var(--pv-raised); border-color: var(--pv-border);
  border-bottom: 2px solid var(--pv-good); color: var(--pv-good); }

/* 3D viewport + overlay chrome (#ViewportOverlay: opaque, square, solid
   border - it sits over a live scene and must not tint it). */
.pv-viewport { position: relative; flex: 1; min-height: 330px;
  background: var(--pv-view); overflow: hidden; }
.pv-overlay { position: absolute; top: 8px; background: var(--pv-panel);
  border: 1px solid var(--pv-border); padding: 0 0 6px; z-index: 2; }
.pv-overlay .pv-cap { display: block; height: 28px; line-height: 28px;
  padding: 0 8px; background: var(--pv-head);
  border-bottom: 1px solid var(--pv-border); margin-bottom: 6px; }
.pv-show-btn { display: flex; align-items: center; gap: 7px; height: 28px;
  margin: 0 6px 3px; padding: 0 8px; background: var(--pv-panel);
  border: 1px solid var(--pv-border);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--pv-mute); white-space: nowrap; }
/* An enabled filter is a STATE: raised tone + its own category square. It is
   not a primary action, so it does not get the amber fill. */
.pv-show-btn.on { background: var(--pv-raised); color: var(--pv-text); }
.pv-show-btn i { width: 6px; height: 6px; flex-shrink: 0; }
.pv-rail-btn { display: block; width: 44px; text-align: center; height: 28px;
  line-height: 26px; margin: 0 6px 3px; background: var(--pv-panel);
  border: 1px solid var(--pv-border);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--pv-mute); }
/* Square marks, like every other mark in the scene (see _add_land_dots). */
.pv-sat { position: absolute; width: 4px; height: 4px; }

/* Legend + Time Machine + timeline. */
.pv-dot { display: inline-block; width: 6px; height: 6px; margin-right: 6px;
  vertical-align: 0; }
.pv-tl { position: relative; height: 40px; background: var(--pv-ink);
  border: 1px solid var(--pv-border); overflow: hidden;
  background-image: repeating-linear-gradient(90deg,
    var(--pv-rule) 0 1px, transparent 1px 46px); }
/* The playhead is a 2px ACCENT_RULE mark - the second and last amber on the
   screen after the PLAY fill. */
.pv-tl-now { position: absolute; left: 50%; top: 0; bottom: 0; width: 2px;
  background: var(--pv-accent-rule); }
.pv-tl-mark { position: absolute; top: 50%; width: 6px; height: 6px;
  transform: translateY(-50%); }

/* Sparkline: a 28px filled band under a 1px rule. The gradient, the stroke
   taper and the terminal dot are gone - a trend is a shape, and three ways of
   drawing the same shape is two too many. */
.pv-spark { display: block; width: 100%; height: 28px; }

/* QStatusBar: ROW_STATUS(22), mono 11px readouts separated by 1px rules. */
.pv-statusbar { display: flex; align-items: center; min-height: 22px;
  padding: 0 10px; background: var(--pv-deep);
  border-top: 1px solid var(--pv-border);
  font-family: 'IBM Plex Mono', 'JetBrains Mono', monospace;
  font-size: 11px; color: var(--pv-mute); flex-wrap: wrap; }
.pv-statusbar span { padding: 0 10px; border-right: 1px solid var(--pv-border); }
.pv-statusbar span.pv-gap { flex: 1; border-right: none; padding: 0; }
.pv-statusbar span:last-child { border-right: none; }
.pv-statusbar .crit { color: var(--pv-rust); }

/* ---- rigid scaling ------------------------------------------------------
   The facsimile is pinned to the app's native 1136px and shrunk as a single
   unit, never reflowed. It used to collapse .pv-body to one column and hide
   the menu/search below 1020px, but a three-rail console stacked into a
   2065px-tall strip is not what the app looks like on any machine - on a
   phone the tab strip, segments and search burst straight out of the window
   frame. Scaling keeps every proportion identical at every resolution; the
   trade is that on a narrow phone it reads as a thumbnail, which is the
   honest thing for a screenshot to do.

   app.js/initConsoleScale sets --pv-scale from .pv-fit's width and gives the
   wrapper the scaled height (a transform doesn't affect layout, so without
   that the section would reserve the full unscaled 776px). Script-off falls
   back to scale 1 clipped by overflow, i.e. the left edge of the console. */
.pv-fit { width: 100%; overflow: hidden; }
.pv-window {
  width: 1136px;
  transform-origin: top left;
  transform: scale(var(--pv-scale, 1));
}

@media (max-width: 900px) {
  .acct-grid { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .acct-wrap { padding: 40px 20px 64px; }
  .acct-auth-card, .acct-card { padding: 22px; }
  .acct-device { flex-wrap: wrap; }
}

/* =========================================================================
   Responsive layer for the marketing pages.
   The marketing templates keep their layout inline on each element (faithful
   to the prototype), and an inline style outranks any stylesheet rule - so the
   small-screen overrides below must use !important to win. Desktop is left
   untouched; nothing here engages until a breakpoint is crossed. Grid
   containers opt in with a class (.r-split / .r-cards-3 / .r-cards-4 /
   .r-fields / .r-footer); oversized display headings scale fluidly via
   clamp() edited inline on the element itself.
   ========================================================================= */

/* ---- mobile navigation (hamburger) --------------------------------------
   The nav links + CTA live in .nav-menu. On desktop that stays the inline
   flex row it already is; below 760px it collapses into a panel toggled by
   the .nav-burger button (app.js flips .open). */
.nav-burger { display: none; }
.nav-burger span, .nav-burger span::before, .nav-burger span::after {
  display: block; width: 18px; height: 2px; border-radius: 2px;
  background: #C7C9CE; transition: background .2s ease;
}
.nav-burger span { position: relative; }
.nav-burger span::before, .nav-burger span::after { content: ''; position: absolute; left: 0; }
.nav-burger span::before { top: -6px; }
.nav-burger span::after  { top: 6px; }
.nav-burger:hover span, .nav-burger:hover span::before, .nav-burger:hover span::after { background: #F3F1EC; }

@media (max-width: 760px) {
  /* Pin the row deterministically: the absolutely-positioned .nav-menu can
     otherwise skew justify-content:space-between (some engines still count it
     as a participant), so pack from the start and push the burger right with
     an auto margin instead. */
  nav > div { justify-content: flex-start !important; }
  .nav-burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 42px; height: 42px; flex-shrink: 0; padding: 0; cursor: pointer;
    margin-left: auto;
    background: rgba(255,255,255,0.04);
    border: 1px solid rgba(255,255,255,0.1); border-radius: 9px;
  }
  .nav-menu {
    position: absolute; top: 100%; left: 0; right: 0;
    display: none !important; flex-direction: column; align-items: stretch !important; gap: 2px;
    background: #0c0d11;
    border-bottom: 1px solid rgba(255,255,255,0.08);
    box-shadow: 0 26px 46px rgba(0,0,0,0.55);
    padding: 12px 20px 18px;
  }
  .nav-menu.open { display: flex !important; }
  .nav-menu .hv-nav-link { padding: 12px 12px !important; font-size: 15px !important; border-radius: 9px; }
  .nav-menu [data-nav-request] {
    margin-left: 0 !important; margin-top: 8px !important;
    justify-content: center; width: 100%;
  }
  .nav-menu [data-nav-dash] {
    margin-left: 0 !important; margin-top: 8px !important;
    justify-content: center; width: 100%;
  }
}
@media (max-width: 420px) {
  nav > div { padding-left: 20px !important; padding-right: 20px !important; }
}

/* ---- responsive grids (opt-in classes on the inline grid containers) ---- */
@media (max-width: 820px) {
  .r-split { grid-template-columns: 1fr !important; }
}
@media (max-width: 900px) {
  .r-cards-3 { grid-template-columns: repeat(2, 1fr) !important; }
  .r-cards-4 { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (max-width: 600px) {
  .r-cards-3 { grid-template-columns: 1fr !important; }
}
@media (max-width: 560px) {
  .r-cards-4 { grid-template-columns: 1fr !important; }
  .r-fields  { grid-template-columns: 1fr !important; }
}

/* footer: 4 columns -> 2 -> 1 */
@media (max-width: 760px) {
  .r-footer { grid-template-columns: 1fr 1fr !important; gap: 28px !important; }
}
@media (max-width: 460px) {
  .r-footer { grid-template-columns: 1fr !important; }
}

/* ---- small-screen tidy-ups ---- */
/* decorative "how it works" connector line only makes sense in the 4-across
   layout; drop it once the steps start stacking. */
@media (max-width: 900px) {
  .r-hide-sm { display: none !important; }
}
@media (max-width: 560px) {
  /* hero stat strip: wrap and drop the vertical dividers so the labels fit */
  .r-hero-stats { gap: 18px 28px !important; flex-wrap: wrap; }
  .r-hero-sep { display: none !important; }
  /* CTA bands carry generous inline padding; pull it in on phones */
  .r-cta-band {
    padding-left: 24px !important; padding-right: 24px !important;
    padding-top: 44px !important; padding-bottom: 44px !important;
  }
}
