/* ============================================================ 1-SETTINGS / PRIMITIVES â€” raw values,theme-AGNOSTIC Never referenced directly by components. Only the semantic contract (2-semantic) maps these into roles. ============================================================ */ :root{
  /* ---- color primitives ---- */
  --c-ink-900:#08080c;  --c-ink-800:#101018;  --c-ink-700:#1a1a24;
  --c-white:#ffffff;
  --c-violet-500:#6d5efc; --c-violet-600:#8b5cf6;
  --c-cyan-400:#22d3ee;   --c-pink-400:#f472b6;
  --c-gray-500:#6c6a78;

  /* ---- spacing scale (8pt) ---- */
  --sp-1:4px; --sp-2:8px; --sp-3:16px; --sp-4:24px;
  --sp-5:32px; --sp-6:48px; --sp-7:64px; --sp-8:96px;

  /* ---- radius scale ---- */
  --rad-1:10px; --rad-2:16px; --rad-3:26px; --rad-pill:100px;

  /* ---- elevation scale ---- */
  --sh-1:0 4px 12px rgba(0,0,0,.15);
  --sh-2:0 20px 50px rgba(0,0,0,.30);
  --sh-pop:0 8px 24px rgba(109,94,252,.32);

  /* ---- type stacks ---- */
  --font-serif-stack:'Instrument Serif',Georgia,serif;
  --font-sans-stack:'Inter',system-ui,sans-serif;

  /* ---- motion ---- */
  /*
   * Two motion scales, deliberately, because they do different jobs.
   *
   * --dur-* is INTERACTION: a hover, a focus ring, a border warming up.
   * These want to feel instant, and design-system-v3 uses its own
   * --transition-fast (0.2s) and --transition (0.3s) for exactly this.
   *
   * --duration-* is ENTRANCE: the animate-* family, a thing arriving on the
   * page. Those are the values design-system-v3 §20 documents, and they are
   * slower on purpose. Renaming --dur-base to 0.45s to "match the spec"
   * would make every hover on the site take almost half a second, which is
   * conformance to the letter and a worse product.
   */
  --dur-fast:.2s; --dur-base:.25s; --dur-slow:.35s;
  --duration-fast:.2s; --duration-base:.45s; --duration-slow:.8s;
  --ease-out-back:cubic-bezier(.34,1.56,.64,1);
  --ease-in-out-soft:cubic-bezier(.45,0,.55,1);
  --ease-standard:cubic-bezier(.4,0,.2,1);
  --ease-emphasized:cubic-bezier(.2,.6,.2,1);
}

/* ============================================================ 2-SEMANTIC / CONTRACT â€” the role-based tokens that pages and components consume. THIS is what a theme overrides. Default theme lives here in :root. Named themes override these same names under [data-theme="x"]. ============================================================ */ :root{
  /* ---------- color roles ---------- */
  --color-surface:        var(--c-ink-900);
  --color-surface-2:      var(--c-ink-800);
  --color-surface-raised: rgba(255,255,255,.03);   /* card glass fill */
  --color-text:           var(--c-white);
  --color-text-muted:     #6c6a78;                 /* solid muted gray (default theme) */
  --color-text-dim:       rgba(255,255,255,.65);   /* body copy on dark */
  --color-text-subtle:    rgba(255,255,255,.55);   /* subtle light text on dark */
  --color-text-faint:     rgba(255,255,255,.45);
  --color-border:         rgba(255,255,255,.1);
  --color-hairline:       rgba(255,255,255,.08);   /* faint divider on dark */
  --color-brand:          var(--c-violet-500);
  --color-brand-2:        var(--c-cyan-400);
  --color-brand-2-tint:   rgba(34,211,238,.15);    /* badge fill */
  --color-brand-2-line:   rgba(34,211,238,.3);     /* badge border */
  --color-accent:         var(--c-pink-400);
  /* status roles */
  --color-danger:         #dc2626;
  --color-success:        #15803d;
  --color-success-bg:     #eefcf3;
  --color-success-bd:     #bbf0cf;
  --gradient-brand:       linear-gradient(90deg,#6d5efc 0%,#8b5cf6 35%,#22d3ee 100%);
  --gradient-brand-soft:  linear-gradient(90deg,rgba(109,94,252,.16),rgba(34,211,238,.12));
  /* ── Fluid-gradient parameters — tune the whole site here ──
     Readability rules: never animate mark.kw-lit (highlighter must stay
     legible); no duration below 6s (fast shimmer harms reading); one
     gradient, one 90° angle, linear easing only (the 4-stop loop tiles
     seamlessly only at constant speed). Reduced-motion freezes all. */
  --gf-stretch:  260% 100%;
  --gf-dur-text: 6s;
  --gf-dur-btn:  8s;
  --gf-dur-fill: 8s;
  --gf-ease:     linear;

  /* ---------- typography ---------- */
  --font-heading: var(--font-serif-stack);
  --font-body:    var(--font-sans-stack);

  /* ---------- shape ---------- */
  --radius-button: var(--rad-pill);
  --radius-card:   var(--rad-2);
  --radius-input:  12px;                            /* form fields / alerts */
  --radius-banner: 36px;                            /* CTA banner */

  /* ---------- depth / effects ---------- */
  --shadow-card: var(--sh-2);
  --shadow-pop:  var(--sh-pop);
  --blur-glass:  16px;
  --glow-line:   rgba(34,211,238,.5);     /* card hover glow border */
  --shadow-glow: 0 0 20px rgba(34,211,238,.2);

  /* ---------- CTA glass banner ---------- */
  --cta-glass-tint:  rgba(8,8,12,.55);    /* dark frost over the orb */
  --cta-glass-solid: rgba(8,8,12,.82);    /* fallback when no backdrop-filter */
  --cta-glass-blur:  18px;
  --cta-orb-size:    300px;
  --cta-orb-opacity: .65;
  --cta-orb-dur-x:   9s;
  --cta-orb-dur-y:   7s;

  /* ---------- motion ---------- */
  --motion-duration: var(--dur-base);
  --motion-ease:     var(--ease-standard);
  --dur-reveal:      .7s;                    /* scroll-reveal entrance */
  --ease-reveal:     var(--ease-emphasized);
  --reveal-shift:    34px;                   /* how far elements rise in */
  --lift-shift:      -4px;                   /* hover-lift translateY */

  /* ---------- admin panel ---------- */
  --admin-bg:              #f4f5f9;          /* content area â€” slightly cool white */
  --admin-card-bg:         #ffffff;
  --admin-sidebar-bg:      #0d0d2b;          /* deep indigo-navy â€” matches public hero */
  --admin-sidebar-grad:    linear-gradient(180deg,#0d0d2b 0%,#141438 100%);
  --admin-sidebar-text:    rgba(255,255,255,.55);
  --admin-sidebar-text-bright: #ffffff;
  --admin-sidebar-text-dim: rgba(255,255,255,.35);
  --admin-sidebar-hairline: rgba(255,255,255,.06);
  --admin-sidebar-btn-bg: rgba(255,255,255,.06);
  --admin-sidebar-btn-border: rgba(255,255,255,.10);
  --admin-sidebar-btn-text: rgba(255,255,255,.75);
  --admin-sidebar-btn-hover-bg: rgba(255,255,255,.14);
  --admin-accent:          #6366f1;          /* indigo â€” same as public site */
  --admin-accent-dark:     #4f46e5;
  --admin-accent-glow:     rgba(99,102,241,.15);
  --admin-accent-ring:     rgba(99,102,241,.18);
  --admin-text:            #0f0f1a;
  --admin-text-muted:      #6b7280;
  --admin-border:          #e5e7eb;
  --admin-border-dark:     #f3f4f6;
  --admin-hover-bg:        rgba(99,102,241,.08);  /* indigo tint on hover */
  --admin-shadow-card:     0 1px 3px rgba(15,15,26,.06),0 4px 16px rgba(15,15,26,.04);
  --admin-shadow-pop:      0 8px 32px rgba(99,102,241,.12);
  --admin-danger:          #b91c1c;
  --admin-danger-bg:       #fef2f2;
  --admin-success:         #15803d;
  --admin-success-bg:      #f0fdf4;
  --admin-success-border:  #bbf7d0;
  /* design-system tokens — static fallbacks; ThemeService overrides the tone/palette-driven ones */
  --admin-input-bg:            #ffffff;                /* input/textarea fill; T aliases --admin-card-bg */
  --admin-text-on-accent:      #ffffff;                /* text/icon on accent gradients (static) */
  --admin-border-hover:        #c7cad2;                /* input border on hover */
  --admin-table-row-hover:     rgba(99,102,241,.04);   /* table row hover tint */
  --admin-accent-text:         var(--admin-accent);    /* accent used AS text (badges/links) — T darkens for contrast */
  --admin-sidebar-hover-bg:    rgba(99,102,241,.12);   /* sidebar nav hover fill */
  --admin-sidebar-logo-tile-bg:#ffffff;                /* light plate behind logo PNG (the one legit white) */
  --admin-danger-border:       #fecaca;                /* danger button/alert border */
  --admin-danger-bg-hover:     #fecaca;                /* danger button hover fill */

  /* ---------- spacing rhythm ---------- */
  --space-section: var(--sp-8);
  --space-gap:     var(--sp-4);

  /* ===========================================================
     SCALE TOKENS consumed by existing pages.
     Defining these FIXES the 68 previously-undefined references
     (--space-* / --radius-* / --font-serif) with zero page edits.
     =========================================================== */
  --space-xs:  var(--sp-1);
  --space-sm:  var(--sp-2);
  --space-md:  var(--sp-3);
  --space-lg:  var(--sp-4);
  --space-xl:  var(--sp-5);
  --space-2xl: var(--sp-6);
  --space-3xl: var(--sp-7);
  --space-4xl: var(--sp-8);

  --radius-sm: var(--rad-1);
  --radius-md: var(--rad-2);
  --radius-lg: var(--rad-3);

  /* ---------- legacy aliases (keep old names working) ---------- */
  --font-serif: var(--font-serif-stack);
  --font-sans:  var(--font-sans-stack);

  /* ===========================================================
     LEGACY â†’ SEMANTIC BRIDGE (Phase 2A)
     Every legacy token the pages/components read is now an alias
     of a semantic role. Override the semantic role in a theme and
     the whole site follows â€” without touching a page. These values
     are byte-identical to the old inline :root, so zero visual change.
     =========================================================== */
  --ink:      var(--color-surface);
  --paper:    var(--color-surface);
  --ink-2:    var(--color-surface-2);
  --paper-2:  var(--color-surface-2);
  --line:     var(--color-border);
  --line-dk:  var(--color-border);
  --muted:    var(--color-text-muted);
  --muted-lt: var(--color-text-subtle);
  --violet:   var(--color-brand);
  --cyan:     var(--color-brand-2);
  --pink:     var(--color-accent);
  --serif:    var(--font-heading);
  --sans:     var(--font-body);
  --r:        var(--radius-card);
  --rl:       var(--radius-lg);
  --grad:      var(--gradient-brand);
  --grad-soft: var(--gradient-brand-soft);
  --indigo:   #4f46e5;   /* dead token â€” removed in Phase 5 lock-down */

  /* ---- blob color tokens (theme-switchable) ---- */
  --blob-color-1: #6d5efc;
  --blob-color-2: #22d3ee;
  --blob-color-3: #f472b6;
  /* ---- phrase gradient token ---- */
  --gradient-phrase: linear-gradient(90deg,#6d5efc,#8b5cf6 35%,#22d3ee 70%,#6d5efc);
  /* ---- keyword + ui element tokens ---- */
  --color-kw-mark:      #ffe44d;
  --shadow-brand-mark:  0 4px 14px rgba(109,94,252,.4);
  --color-mock-1:       #0c0c12;
  --color-mock-2:       #16161f;
  /* ---- glass / surface state tokens ---- */
  --color-nav-scrolled-bg:         rgba(250,250,248,.72);
  --color-color-nav-scrolled-text: rgba(10,10,10,.9);
  --color-glass-fill:              rgba(255,255,255,.06);
  --color-glass-border:            rgba(255,255,255,.12);
  --color-surface-hover:           rgba(255,255,255,.08);
  --color-surface-interactive:     rgba(255,255,255,.05);
  --color-surface-interactive-hov: rgba(255,255,255,.10);
  /* ---- text brightness scale gap-fillers ---- */
  --color-text-bright:             rgba(255,255,255,.85);
  --color-text-dim-heavy:          rgba(255,255,255,.30);
  /* ---- brand opacity tints ---- */
  --color-brand-tint:              rgba(109,94,252,.15);
  --color-brand-line:              rgba(109,94,252,.30);
  /* ---- shadow variants ---- */
  --shadow-gradient-hover:         0 12px 32px rgba(109,94,252,.45);
  /* ---- admin UI component tokens ---- */
  --admin-badge-gray-bg:           #f3f4f6;
  --admin-badge-gray-text:         #5b6472;
  --admin-badge-gray-border:       #e5e7eb;
  --admin-badge-indigo-bg:         rgba(99,102,241,.10);
  --admin-badge-indigo-border:     rgba(99,102,241,.20);
  --admin-btn-secondary-bg:        #f0f1ff;
  --admin-btn-secondary-bg-hover:  #e6e7ff;
  --admin-accent-border-hover:     rgba(99,102,241,.40);
  --admin-surface-active:          rgba(99,102,241,.18);
  --shadow-admin-btn:              0 2px 8px rgba(99,102,241,.30);
  --shadow-admin-accent-hover:     0 4px 16px rgba(99,102,241,.40);
  --shadow-admin-accent:           0 4px 12px rgba(99,102,241,.38);
  --shadow-admin-file:             0 2px 6px rgba(99,102,241,.25);
  --gradient-admin-logo:           linear-gradient(90deg,#6366f1,#22d3ee);
}
/* the [data-theme="crimson"] palette was removed with its toggle */


/* ============================================================ 4-COMPONENTS â€” reusable UI,consuming ONLY semantic tokens. Migrated from the layout inline <style> (Phase 2B). Values are byte-identical to the originals â†’ zero visual change. Because these read semantic roles (not raw values),a theme override in _contract.css now reskins every one of them. Page chrome (nav,footer,overlay,mesh,marquee,mock) stays in the inline block until Phase 5 lock-down. ============================================================ */ /* â”€â”€ TYPE HELPERS â”€â”€ */ /* .eyebrow is defined once, in the §4 TYPOGRAPHY block further down, at the
   reference's own 11px / 700 / 2.5px / uppercase. An earlier .78rem/500
   version lived here with a decorative ::before rule; both were overridden
   and neither had applied to anything. The later block cancels the ::before
   explicitly, which is the tell. */
.eyebrow.on-dark{color:var(--color-text-subtle)}
/* The heading scale is defined once, further down, in the px steps
   design-system-v3 specifies. An earlier rem-clamp version of .h-xl/.h-lg/.h-md
   lived here and had never applied to anything -- it was overridden by that
   later block, which is the whole reason both could drift apart unnoticed. */
/* .serif-accent's family, style, weight and colour are set once, further down.
   Only the tracking survives from here: the later rule omits it, so removing
   this outright would loosen the italic everywhere it is used — which is the
   site's signature typographic move. */
.serif-accent{letter-spacing:-.01em}
@keyframes gradFlow{0%{background-position:0% center}100%{background-position:100% center}}
.grad-text{
  background:var(--gradient-phrase);
  background-size:var(--gf-stretch);
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  animation:gradFlow var(--gf-dur-text) var(--gf-ease) infinite;
}
.grad-fill-flow{
  background:var(--gradient-phrase);
  background-size:var(--gf-stretch);
  animation:gradFlow var(--gf-dur-fill) var(--gf-ease) infinite;
}
@media(prefers-reduced-motion:reduce){
.grad-text,.grad-fill-flow,.btn-grad{animation:none;background-position:0% center}
}
/* .lead is defined once, further down, at the reference's 18px/1.7. An earlier
   1.15rem version lived here and had never applied to anything. */
.lead.on-dark{color:var(--color-text-subtle)}
/* .body likewise -- the live one is further down at 16px/1.6. */

/* â”€â”€ LAYOUT â”€â”€ */ .wrap{max-width:1240px;margin:0 auto;padding:0 40px}
.sec{padding:130px 0}
.sec-sm{padding:90px 0}

/* â”€â”€ BUTTONS â”€â”€ */ /* â”€â”€ CARDS â”€â”€ */ .card{background:var(--color-surface-raised);border:1px solid var(--color-border);border-radius:var(--radius-lg);
  /* Toned sections restate this; the base needs it for pages that never
     adopted <x-section>, where cards were rendering with no padding. */
  padding:var(--space-4);
  transition:all var(--dur-slow) var(--ease-standard);color:var(--color-text)}
.card .body,.card .lead,.card .eyebrow{color:var(--color-text-subtle)}
.card:hover{transform:translateY(-4px);box-shadow:var(--shadow-card);border-color:rgba(255,255,255,.2)}
.glass{background:var(--color-glass-fill);backdrop-filter:blur(var(--blur-glass));border:1px solid var(--color-glass-border);border-radius:var(--radius-card)}
.chip{display:inline-flex;align-items:center;gap:6px;font-size:.78rem;font-weight:450;padding:6px 13px;border-radius:var(--radius-button);
  border:1px solid var(--color-border);color:var(--color-text-dim);background:var(--color-glass-fill)}
.card-solid{background:var(--color-surface);border-color:transparent}

/* â”€â”€ FORM â”€â”€ */ .field{width:100%;padding:13px 16px;border:1px solid var(--color-border);border-radius:var(--radius-input);
  font-family:var(--font-body);font-size:.95rem;color:var(--color-text);background:var(--color-surface);
  outline:none;transition:border-color var(--dur-fast) var(--ease-standard)}
.field:focus{border-color:var(--color-brand)}
.field::placeholder{color:var(--color-text-muted);opacity:1}
textarea.field{resize:none}
.field-label{display:block;font-size:.8rem;font-weight:500;margin-bottom:8px}
.field-error{color:var(--color-danger);font-size:.78rem;margin-top:5px}
.alert-ok{padding:16px 20px;background:var(--color-success-bg);border:1px solid var(--color-success-bd);
  color:var(--color-success);border-radius:var(--radius-input);margin-bottom:28px;font-size:.9rem}

/* â”€â”€ DEMO LIST (info tile) â”€â”€ */ .demo-item{display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--color-hairline);font-size:.9rem}
.demo-item .arrow{color:var(--color-brand-2)}

/* â”€â”€ SURFACE CARD + GLOW HOVER + SECTION HEADING â”€â”€ */ .surface-card{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-raised)}
.glow-hover{transition:border-color var(--dur-base) var(--ease-standard),box-shadow var(--dur-base) var(--ease-standard)}
.glow-hover:hover{border-color:var(--glow-line);box-shadow:var(--shadow-glow)}
.industry-tile{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface-raised);
  text-align:center;padding:var(--space-lg)}
.back-link{display:inline-flex;align-items:center;gap:8px;color:var(--color-text-subtle);font-size:.85rem}
.back-link:hover{color:var(--color-text)}
/* .h-section is defined with the rest of the scale, further down. */

/* â”€â”€ CARD CONTENT â”€â”€ */ .card-icon{font-size:2rem;margin-bottom:18px}
.card-title{font-size:1.3rem;font-weight:600;letter-spacing:-.02em;margin-bottom:10px}

/* â”€â”€ STAT â”€â”€ */ .stat-num{font-size:2.6rem;font-weight:600;letter-spacing:-.03em}
.stat-label{font-size:.82rem;color:var(--color-text-subtle);margin-top:6px}

/* â”€â”€ TEAM / MEDIA CARD â”€â”€ */ .media-avatar{aspect-ratio:1;border-radius:20px;background:var(--gradient-brand-soft);display:grid;place-items:center;overflow:hidden}
.media-avatar img{width:100%;height:100%;object-fit:cover}
.team-name{font-weight:600;color:var(--color-text)}
.team-role{font-size:.84rem;color:var(--color-text-subtle)}

/* â”€â”€ BLOG â”€â”€ */ .post-thumb{aspect-ratio:16/10;background:var(--gradient-brand-soft);display:grid;place-items:center;overflow:hidden}
.post-thumb img{width:100%;height:100%;object-fit:cover}
.post-title{font-size:1.2rem;font-weight:600;letter-spacing:-.01em;line-height:1.3;margin-bottom:10px}
.post-meta{display:flex;justify-content:space-between;font-size:.78rem;color:var(--color-text-muted)}
/* .prose's type is set once, further down, at the reference's 65ch / 16px /
   1.8. Only the centring from the older rule still does any work — the later
   block omits margin, so deleting this whole rule would un-centre every
   prose block on the site. */
.prose{margin:0 auto}

/* â”€â”€ PRODUCT ROW â”€â”€ */ .product-row{display:block;padding:var(--space-2xl);margin-bottom:var(--space-lg);
  border:1px solid var(--color-border);border-radius:var(--radius-lg);background:var(--color-surface-raised)}
.product-name{font-size:1.5rem;font-weight:700;color:var(--color-text);margin:0}
.icon-tile{width:60px;height:60px;min-width:60px;border-radius:var(--radius-card);
  background:linear-gradient(135deg,var(--color-brand),var(--color-brand-2));
  display:flex;align-items:center;justify-content:center;font-size:1.8rem}
.badge-featured{display:inline-flex;align-items:center;gap:6px;font-size:.75rem;font-weight:500;padding:6px 13px;
  border-radius:var(--radius-button);background:var(--color-brand-2-tint);color:var(--color-brand-2);
  border:1px solid var(--color-brand-2-line);text-transform:uppercase;letter-spacing:.05em}
.tag{font-size:.72rem;padding:4px 11px;border-radius:var(--radius-button);background:var(--color-surface-interactive);
  color:var(--color-text-dim);border:1px solid var(--color-border)}

/* â”€â”€ CTA BANNER â”€â”€ */ /* â”€â”€ ADMIN PANEL â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€ */ /* Layout shell */ .admin-wrapper{display:flex;min-height:100vh;background:var(--admin-bg);color:var(--admin-text);font-family:'DM Sans',sans-serif}

/* â”€â”€ Sidebar â”€â”€ */ .admin-sidebar{width:252px;background:var(--admin-sidebar-grad,var(--admin-sidebar-bg));color:#fff;flex-shrink:0;display:flex;flex-direction:column;position:sticky;top:0;height:100vh}

/* Logo block */ .admin-sidebar-logo{padding:28px 24px 24px;border-bottom:1px solid rgba(255,255,255,.07)}
.admin-sidebar-logo-inner{display:flex;align-items:center;gap:10px}
.admin-sidebar-logo-icon-wrapper{background:var(--admin-sidebar-logo-tile-bg);border-radius:12px;padding:8px;display:flex;align-items:center;justify-content:center}
.admin-sidebar-logo-icon{width:34px;height:34px;border-radius:10px;background:var(--gradient-admin-logo);display:flex;align-items:center;justify-content:center;font-weight:900;font-size:.95rem;color:#fff;flex-shrink:0;box-shadow:var(--shadow-admin-accent)}
.admin-sidebar-logo-text{font-size:1rem;font-weight:700;color:#fff;letter-spacing:-.01em}
.admin-sidebar-logo-text span{display:block;font-size:.65rem;color:rgba(255,255,255,.6);font-weight:400;letter-spacing:.07em;text-transform:uppercase;margin-top:1px}

/* Nav */ .admin-sidebar-nav{padding:16px 0;flex:1;overflow-y:auto}
.admin-sidebar-nav a{display:flex;align-items:center;gap:10px;padding:10px 20px;margin:1px 12px;border-radius:10px;font-size:.875rem;color:var(--admin-sidebar-text);text-decoration:none;transition:all .18s;font-weight:450}
.admin-sidebar-nav a:hover{color:#fff;background:var(--admin-sidebar-hover-bg)}
.admin-sidebar-nav a.active{color:#fff;background:var(--admin-surface-active);font-weight:600;box-shadow:inset 3px 0 0 var(--admin-accent)}
.admin-sidebar-divider{height:1px;background:var(--admin-sidebar-hairline);margin:8px 24px}
.admin-sidebar-section{padding:14px 24px 6px;font-size:.66rem;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:var(--admin-sidebar-text)}

/* Sidebar footer */ .admin-sidebar-footer{padding:16px 24px 20px;border-top:1px solid var(--admin-sidebar-hairline)}
.admin-sidebar-footer-name{color:var(--admin-sidebar-text-bright);font-size:.82rem;font-weight:600}
.admin-sidebar-footer-role{color:var(--admin-sidebar-text);font-size:.7rem;margin-top:1px;margin-bottom:10px}
.admin-sidebar-footer-text{color:var(--admin-sidebar-text-dim);font-size:.78rem;margin-bottom:10px}
.admin-sidebar-footer a{font-size:.8rem;color:var(--admin-sidebar-text);text-decoration:none}
.admin-sidebar-logout-btn{background:var(--admin-sidebar-btn-bg);border:1px solid var(--admin-sidebar-btn-border);color:var(--admin-sidebar-btn-text);font-size:.78rem;cursor:pointer;padding:6px 14px;font-family:inherit;border-radius:8px;transition:all .18s;width:100%;text-align:left;margin-top:8px}
.admin-sidebar-logout-btn:hover{background:var(--admin-sidebar-btn-hover-bg);color:var(--admin-sidebar-text-bright)}

/* â”€â”€ Main area â”€â”€ */ /* overflow-x:clip, not hidden. `hidden` forces the other axis to `auto`, which
   makes this a scroll container -- and a sticky topbar inside a scroll
   container that never scrolls can never stick. `clip` leaves overflow-y
   visible and still prevents sideways overflow. */
.admin-main{flex:1;overflow-x:clip;display:flex;flex-direction:column;min-width:0}

/* Topbar */ .admin-topbar{background:var(--admin-card-bg);border-bottom:1px solid var(--admin-border);padding:0 40px;height:60px;display:flex;justify-content:space-between;align-items:center;position:sticky;top:0;z-index:10;box-shadow:0 2px 8px rgba(0,0,0,.06)}
.admin-page-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:28px;padding-bottom:20px;border-bottom:1px solid var(--admin-border)}
.admin-page-title{font-size:1.1rem;font-weight:700;color:var(--admin-text);letter-spacing:-.01em}
.admin-topbar h1{font-size:.95rem;font-weight:600;color:var(--admin-text);letter-spacing:-.01em}
.admin-topbar-link{font-size:.8rem;color:var(--admin-text-muted);text-decoration:none;display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:1px solid var(--admin-border);border-radius:8px;transition:all .18s}
.admin-topbar-link:hover{border-color:var(--admin-accent);color:var(--admin-accent-text)}
.admin-content{padding:36px 40px;flex:1}

/* â”€â”€ Cards â”€â”€ */ .admin-card{background:var(--admin-card-bg);border:1px solid var(--admin-border);border-radius:16px;padding:28px;margin-bottom:24px;box-shadow:var(--admin-shadow-card)}

/* â”€â”€ Tables â”€â”€ */ .admin-table{width:100%;border-collapse:collapse;font-size:.875rem}
.admin-table th{text-align:left;padding:10px 16px;border-bottom:1px solid var(--admin-border);font-weight:600;font-size:.72rem;letter-spacing:.07em;text-transform:uppercase;color:var(--admin-text-muted);background:var(--admin-bg)}
.admin-table td{padding:13px 16px;border-bottom:1px solid var(--admin-border-dark)}
.admin-table tbody tr{transition:background .12s}
.admin-table tbody tr:hover td{background:var(--admin-table-row-hover)}
.admin-table tbody tr:last-child td{border-bottom:none}

/* â”€â”€ Badges â”€â”€ */ .admin-badge{display:inline-flex;align-items:center;padding:3px 10px;border-radius:100px;font-size:.7rem;font-weight:600;letter-spacing:.02em}
.admin-badge-green{background:var(--admin-success-bg);color:var(--admin-success);border:1px solid var(--admin-success-border,#bbf7d0)}
.admin-badge-gray{background:var(--admin-badge-gray-bg);color:var(--admin-badge-gray-text);border:1px solid var(--admin-badge-gray-border)}
.admin-badge-indigo{background:var(--admin-badge-indigo-bg);color:var(--admin-accent-text);border:1px solid var(--admin-badge-indigo-border)}

/* â”€â”€ Buttons â”€â”€ */ .admin-btn{display:inline-flex;align-items:center;gap:6px;padding:9px 18px;border-radius:10px;font-size:.82rem;font-weight:500;cursor:pointer;text-decoration:none;border:none;transition:all .18s;font-family:inherit;white-space:nowrap}
.admin-btn-dark{background:linear-gradient(135deg,var(--admin-accent) 0%,var(--admin-accent-dark) 100%);color:var(--admin-text-on-accent);box-shadow:var(--shadow-admin-btn)}
.admin-btn-dark:hover{box-shadow:var(--shadow-admin-accent-hover);transform:translateY(-1px);color:#fff}
.admin-btn-danger{background:var(--admin-danger-bg);color:var(--admin-danger);border:1px solid var(--admin-danger-border)}
.admin-btn-danger:hover{background:var(--admin-danger-bg-hover)}
.admin-btn-secondary{background:var(--admin-btn-secondary-bg);color:var(--admin-accent-text);border:1px solid var(--admin-badge-indigo-border)}
.admin-btn-secondary:hover{background:var(--admin-btn-secondary-bg-hover);border-color:var(--admin-accent-border-hover);color:var(--admin-accent-dark)}

/* â”€â”€ Alerts â”€â”€ */ .admin-alert{padding:12px 18px;border-radius:12px;margin-bottom:20px;font-size:.875rem;font-weight:500}
.admin-alert-success{background:var(--admin-success-bg);color:var(--admin-success);border:1px solid var(--admin-success-border,#bbf7d0)}
.admin-alert-error{background:var(--admin-danger-bg);color:var(--admin-danger);border:1px solid var(--admin-danger-border)}

/* â”€â”€ Stat boxes (dashboard) â”€â”€ */ .admin-stat-box{background:var(--admin-card-bg);border:1px solid var(--admin-border);border-radius:16px;padding:28px 32px;box-shadow:var(--admin-shadow-card);border-top:3px solid var(--admin-accent);transition:box-shadow .18s}
.admin-stat-box:hover{box-shadow:var(--admin-shadow-pop)}
.admin-stat-box .num{font-size:2.4rem;font-weight:800;margin-bottom:4px;color:var(--admin-text);letter-spacing:-.02em}
.admin-stat-box .lbl{font-size:.75rem;color:var(--admin-text-muted);text-transform:uppercase;letter-spacing:.08em;font-weight:600}
.admin-grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;margin-bottom:36px}

/* â”€â”€ Form structure â”€â”€ */ .admin-form-section{margin-bottom:40px;padding-bottom:32px;border-bottom:1px solid var(--admin-border)}
.admin-form-section:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}
.admin-form-section h3{font-size:.95rem;font-weight:700;margin-bottom:24px;color:var(--admin-text);display:flex;align-items:center;gap:8px}
.admin-form-section h3::before{content:'';display:inline-block;width:3px;height:16px;background:var(--admin-accent);border-radius:2px}
.admin-fieldset{display:grid;gap:20px}
.admin-fieldset.cols-2{grid-template-columns:repeat(2,1fr)}
.admin-fieldset.cols-3{grid-template-columns:repeat(3,1fr)}

/* Form groups */ .admin-form-group{margin-bottom:20px;display:flex;flex-direction:column}
.admin-form-group.error .admin-form-control{border-color:var(--admin-danger);background:rgba(220,38,38,.02)}
.admin-form-group.success .admin-form-control{border-color:var(--admin-success);background:rgba(21,128,61,.02)}

/* Labels */ .admin-form-group label{display:block;font-size:.72rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:var(--admin-text-muted);margin-bottom:7px}
.admin-form-group label .required{color:var(--admin-danger);margin-left:2px}

/* Input controls */ .admin-form-control{width:100%;padding:10px 14px;border:1.5px solid var(--admin-input-border,var(--admin-border));border-radius:10px;font-family:inherit;font-size:.9rem;color:var(--admin-text);background:var(--admin-input-bg);outline:none;transition:border-color .18s,box-shadow .18s,background .18s}
.admin-form-control:hover{border-color:var(--admin-input-border-hover,var(--admin-border-hover))}
.admin-form-control:focus{border-color:var(--admin-accent);box-shadow:0 0 0 3px var(--admin-accent-ring);outline:none}
.admin-form-control::placeholder{color:var(--admin-text-muted);opacity:.55}
.admin-form-control:disabled{background:var(--admin-bg);color:var(--admin-text-muted);cursor:not-allowed;opacity:.7}

textarea.admin-form-control{resize:vertical;min-height:120px}
select.admin-form-control{cursor:pointer;padding-right:32px;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='%236b7280' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;appearance:none}

/* Checkboxes & radios */ .admin-checkbox,.admin-radio{display:flex;align-items:center;gap:8px;margin-bottom:12px;cursor:pointer}
.admin-checkbox input[type="checkbox"],.admin-radio input[type="radio"]{cursor:pointer;width:16px;height:16px;accent-color:var(--admin-accent-text)}
.admin-checkbox label,.admin-radio label{margin-bottom:0;text-transform:none;font-weight:400;font-size:.9rem;cursor:pointer;letter-spacing:0}

/* Help / error text */ .admin-form-help{display:block;font-size:.75rem;color:var(--admin-text-muted);margin-top:5px;font-weight:400;line-height:1.5}
.admin-form-error{display:block;font-size:.78rem;color:var(--admin-danger);margin-top:5px;font-weight:500}
.admin-form-success{display:block;font-size:.78rem;color:var(--admin-success);margin-top:5px;font-weight:500}

/* Form actions */ .admin-form-actions{display:flex;gap:12px;margin-top:32px;padding-top:24px;border-top:1px solid var(--admin-border)}
.admin-form-actions-primary{margin-left:auto}

/* File input */ .admin-file-input{display:flex;gap:10px;align-items:center}
.admin-file-input input[type="file"]{display:none}
.admin-file-input-label{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;background:linear-gradient(135deg,var(--admin-accent),var(--admin-accent-dark));color:var(--admin-text-on-accent);border-radius:10px;font-size:.82rem;font-weight:500;cursor:pointer;transition:all .18s;box-shadow:var(--shadow-admin-file)}
.admin-file-input-label:hover{box-shadow:var(--shadow-admin-accent);transform:translateY(-1px)}
.admin-file-input-preview{font-size:.83rem;color:var(--admin-text-muted)}

/* Read-only field */ .admin-form-readonly{padding:10px 14px;background:var(--admin-bg);border:1.5px solid var(--admin-border);border-radius:10px;color:var(--admin-text);font-size:.9rem}

/* ============================================================
   3-MOTION / ANIMATION PRIMITIVE  â€” the SINGLE reveal system.
   Centralized here so every page (incl. home) consumes one
   source of truth. `.reveal` and `.fade-in-up` are aliases of
   the SAME behaviour; `.in` and `.visible` are the same "shown" state. Timing comes from motion tokens,never hardcoded. SAFETY: the hidden pre-reveal state is gated behind `html.js`. If JavaScript (or a CDN animation lib) fails to run,`.js` is never added,the hidden rules never apply,and ALL content stays fully visible. No blank pages,ever. ============================================================ */ /* Shown state â€” default for no-JS,and the target for JS reveal */ .reveal,.fade-in-up{
  opacity:1;
  transform:none;
}

/* Hidden pre-reveal state â€” ONLY when JS is alive. border-color/box-shadow are in the list so a .glow-hover that is ALSO a reveal element keeps a working hover transition (this rule wins the cascade over .glow-hover's own transition). */ .js .reveal,.js .fade-in-up{
  opacity:0;
  transform:translateY(var(--reveal-shift));
  transition:opacity var(--dur-reveal) var(--ease-reveal),
             transform var(--dur-reveal) var(--ease-reveal),
             border-color var(--dur-base) var(--ease-standard),
             box-shadow var(--dur-base) var(--ease-standard);
  will-change:opacity,transform;
}

/* Entered state (added by IntersectionObserver / GSAP) */ .js .reveal.in,.js .fade-in-up.visible{
  opacity:1;
  transform:none;
}

/* Stagger delays â€” both naming schemes supported */ .reveal.d1,.fade-in-up.stagger-1{transition-delay:.08s}
.reveal.d2,.fade-in-up.stagger-2{transition-delay:.16s}
.reveal.d3,.fade-in-up.stagger-3{transition-delay:.24s}
.reveal.d4,.fade-in-up.stagger-4{transition-delay:.32s}

/* Hover lift â€” used by product/skill cards */ .hover-lift{
  transition:transform var(--dur-slow) var(--ease-standard),
             box-shadow var(--dur-slow) var(--ease-standard);
}
.hover-lift:hover{
  transform:translateY(var(--lift-shift));
  box-shadow:var(--shadow-card);
}

/* Hero floating icons â€” gentle float + pulsing glow (revived from dead classes) */ .float-icon{animation:fl-float 6s ease-in-out infinite}
.float-icon.glow-pulse{animation:fl-float 6s ease-in-out infinite, fl-glow 3.4s ease-in-out infinite}
.glow-pulse.cyan{--fl-glow:rgba(34,211,238,.55)}
.glow-pulse.pink{--fl-glow:rgba(244,114,182,.55)}
.glow-pulse.violet{--fl-glow:rgba(109,94,252,.55)}
.float-icon.f1{animation-delay:0s,0s}
.float-icon.f2{animation-delay:-2s,-1s}
.float-icon.f3{animation-delay:-3.5s,-2s}
.float-icon.f4{animation-delay:-1s,-2.6s}
@keyframes fl-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-16px)}}
@keyframes fl-glow{0%,100%{box-shadow:0 0 0 rgba(0,0,0,0)}50%{box-shadow:0 0 28px var(--fl-glow,rgba(34,211,238,.5))}}

/* Accessibility â€” respect reduced-motion: no transforms, no transitions */
@media (prefers-reduced-motion: reduce){
.js .reveal,.js .fade-in-up{
    opacity:1 !important;
    transform:none !important;
    transition:none !important;
  }
  .hover-lift{transition:none}
  .hover-lift:hover{transform:none}
  .float-icon,.float-icon.glow-pulse{animation:none}
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
}



/* ---- status badge classes ---- */ .badge-beta{display:inline-flex;align-items:center;font-size:.7rem;padding:3px 9px;border-radius:100px;font-weight:600;background:var(--color-brand-2-tint);color:var(--color-brand-2)}
.badge-active{display:inline-flex;align-items:center;font-size:.7rem;padding:3px 9px;border-radius:100px;font-weight:600;background:rgba(34,197,94,.12);color:#22c55e}
.badge-coming-soon{display:inline-flex;align-items:center;font-size:.7rem;padding:3px 9px;border-radius:100px;font-weight:600;background:rgba(107,114,128,.12);color:#9ca3af}
/* ---- admin tab component ---- */ .admin-tabs-nav{display:flex;gap:4px;border-bottom:2px solid var(--admin-border);margin-bottom:28px}
.admin-tabs-nav button{background:none;border:none;padding:10px 20px;font-size:.875rem;font-weight:500;color:var(--admin-text-muted);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;border-radius:6px 6px 0 0;transition:color .15s,border-color .15s}
.admin-tabs-nav button:hover{color:var(--admin-accent-text)}
.admin-tabs-nav button.active{color:var(--admin-accent-text);border-bottom-color:var(--admin-accent-text);font-weight:600}
.admin-tab-panel{display:none}
.admin-tab-panel.active{display:block}


/* ============================================================ ADMIN RESPONSIVE — mobile-first additions ============================================================ */ /* Hamburger: hidden on desktop,flex on mobile */ .admin-hamburger{
  display:none;
  flex-direction:column;
  justify-content:center;
  align-items:center;
  gap:5px;
  width:36px;height:36px;
  background:none;border:none;cursor:pointer;
  flex-shrink:0;margin-right:8px;
  padding:4px;border-radius:8px;
  transition:background .15s;
}
.admin-hamburger:hover{background:var(--admin-btn-secondary-bg)}
.admin-hamburger span{
  display:block;width:20px;height:2px;
  background:var(--admin-text);border-radius:2px;
  transition:transform .2s ease,opacity .2s ease;
}
.admin-wrapper.sidebar-open .admin-hamburger span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.admin-wrapper.sidebar-open .admin-hamburger span:nth-child(2){opacity:0}
.admin-wrapper.sidebar-open .admin-hamburger span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* Overlay (hidden by default,shown when sidebar open on mobile) */ .admin-sidebar-overlay{display:none}

/* Admin table horizontal scroll wrapper */ .admin-table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}

/* ── Tablet 768–1023px ── */
@media(max-width:1023px){
.admin-sidebar{width:220px}
  .admin-topbar{padding:0 24px}
  .admin-content{padding:28px 24px}
  .admin-grid-4{grid-template-columns:repeat(2,1fr)}
}

/* ── Mobile <768px ── */
@media(max-width:767px){
/* Sidebar: off-canvas drawer */ .admin-sidebar{
    position:fixed;top:0;left:0;
    width:272px;height:100vh;
    z-index:200;
    transform:translateX(-100%);
    transition:transform .25s ease;
    box-shadow:none;
  }
  .admin-wrapper.sidebar-open .admin-sidebar{
    transform:translateX(0);
    box-shadow:4px 0 32px rgba(0,0,0,.35);
  }

  /* Overlay */ .admin-sidebar-overlay{
    display:block;
    position:fixed;inset:0;
    z-index:199;
    background:rgba(0,0,0,.45);
    opacity:0;pointer-events:none;
    transition:opacity .25s ease;
  }
  .admin-wrapper.sidebar-open .admin-sidebar-overlay{
    opacity:1;pointer-events:auto;
  }

  /* Hamburger visible */ .admin-hamburger{display:flex}

  /* Topbar */ .admin-topbar{padding:0 16px}

  /* Content */ .admin-content{padding:20px 16px}

  /* Stat grid */ .admin-grid-4{grid-template-columns:repeat(2,1fr);gap:12px;margin-bottom:24px}

  /* Form field grids collapse */ .admin-fieldset.cols-2,.admin-fieldset.cols-3{grid-template-columns:1fr}

  /* Form actions stack */ .admin-form-actions{flex-direction:column-reverse}
  .admin-form-actions-primary{margin-left:0}
  .admin-form-actions .admin-btn{width:100%;justify-content:center}

  /* Page header stacks */ .admin-page-header{flex-direction:column;align-items:flex-start;gap:12px}
  .admin-page-header .admin-btn{width:100%;justify-content:center}

  /* Cards reduce padding */ .admin-card{padding:18px 16px}

  /* Topbar title smaller */ .admin-topbar h1{font-size:.82rem}
}


/* ============================================================ ADMIN — Back button + Breadcrumbs ============================================================ */ .admin-back-btn{
  display:flex;align-items:center;justify-content:center;
  width:32px;height:32px;flex-shrink:0;
  border:1px solid var(--admin-border);
  border-radius:8px;background:var(--admin-btn-secondary-bg);
  color:var(--admin-text);font-size:1rem;line-height:1;
  cursor:pointer;margin-right:4px;
  transition:background .15s,border-color .15s;
}
.admin-back-btn:hover{background:var(--admin-border);border-color:var(--admin-accent-text)}

.admin-breadcrumbs{
  display:flex;align-items:center;gap:6px;
  padding:8px 40px;
  font-size:.78rem;color:var(--admin-text-muted);
  border-bottom:1px solid var(--admin-border);
  background:var(--admin-card-bg);
  position:sticky;top:60px;z-index:9;
  overflow-x:auto;white-space:nowrap;
}
.admin-breadcrumbs a{color:var(--admin-text-muted);text-decoration:none}
.admin-breadcrumbs a:hover{color:var(--admin-accent-text)}
.admin-breadcrumbs .sep{color:var(--admin-border);margin:0 2px}
.admin-breadcrumbs .current{color:var(--admin-text);font-weight:500}

@media(max-width:767px){
.admin-breadcrumbs{padding:8px 16px;top:60px}
}


/* ============================================================ ADMIN — Lead-edit drawer + notes timeline ============================================================ */ .admin-drawer-overlay{
  display:none;position:fixed;inset:0;z-index:299;
  background:rgba(0,0,0,.45);opacity:0;transition:opacity .25s ease;
}
.admin-drawer-overlay.open{display:block;opacity:1}

.admin-drawer{
  position:fixed;top:0;right:0;height:100vh;width:620px;max-width:96vw;
  background:var(--admin-card-bg);border-left:1px solid var(--admin-border);
  box-shadow:-8px 0 32px rgba(0,0,0,.18);
  z-index:300;transform:translateX(100%);transition:transform .25s ease;
  display:flex;flex-direction:column;
}
.admin-drawer .admin-fieldset.cols-2,.admin-drawer .admin-fieldset.cols-3{grid-template-columns:1fr}
.admin-drawer.open{transform:translateX(0)}
.admin-drawer>form{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}

.admin-drawer-header{
  display:flex;align-items:center;justify-content:space-between;
  padding:20px 24px;border-bottom:1px solid var(--admin-border);flex-shrink:0;
}
.admin-drawer-header h3{font-size:.95rem;font-weight:700;color:var(--admin-text)}
.admin-drawer-close{
  background:none;border:none;font-size:1.4rem;line-height:1;cursor:pointer;
  color:var(--admin-text-muted);padding:0 4px;
}
.admin-drawer-close:hover{color:var(--admin-text)}

.admin-drawer-body{padding:20px 24px;overflow-y:auto;flex:1}
.admin-drawer-footer{
  display:flex;gap:10px;justify-content:flex-end;
  padding:16px 24px;border-top:1px solid var(--admin-border);flex-shrink:0;
}

.admin-notes-timeline{margin-top:16px;padding-top:16px;border-top:1px solid var(--admin-border-dark)}
.admin-note-entry{
  border-left:2px solid var(--admin-border);padding:2px 0 12px 14px;margin-bottom:8px;
}
.admin-note-entry:last-child{margin-bottom:0}
.admin-note-meta{font-size:.7rem;color:var(--admin-text-muted);margin-bottom:3px}
.admin-note-text{font-size:.82rem;color:var(--admin-text);line-height:1.5}

@media(max-width:767px){
.admin-drawer{width:100vw;max-width:100vw}
}

/* ============================================================ ADMIN — Full-screen modal (deliberate data-entry forms) Used by: Add Offline Lead ============================================================ */ .admin-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.48);z-index:1000;display:none;align-items:center;justify-content:center;padding:16px}
.admin-modal-overlay.open{display:flex}
.admin-modal{background:var(--admin-card-bg);border-radius:16px;width:min(780px,100%);max-height:90vh;display:flex;flex-direction:column;box-shadow:var(--admin-shadow-pop);overflow:hidden}
.admin-modal>form{display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}
.admin-modal-header{padding:20px 28px 16px;border-bottom:1px solid var(--admin-border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0}
.admin-modal-header h3{font-size:1.05rem;font-weight:700;color:var(--admin-text);margin:0}
.admin-modal-close{background:none;border:none;font-size:1.4rem;line-height:1;color:var(--admin-text-muted);cursor:pointer;padding:0 4px}
.admin-modal-close:hover{color:var(--admin-text)}
.admin-modal-body{padding:24px 28px;overflow-y:auto;flex:1;min-height:0}
.admin-modal-section{margin-bottom:20px}
.admin-modal-section-title{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;color:var(--admin-text-muted);margin-bottom:12px;padding-bottom:6px;border-bottom:1px solid var(--admin-border)}
.admin-modal-footer{padding:16px 28px;border-top:1px solid var(--admin-border);display:flex;justify-content:flex-end;gap:10px;flex-shrink:0;background:var(--admin-card-bg)}
@media(max-width:768px){
.admin-modal{width:100%;max-width:100%;max-height:100vh;border-radius:0;height:100vh}
  .admin-modal-overlay{padding:0;align-items:flex-end}
  .admin-modal-body{padding:16px}
  .admin-modal-header,.admin-modal-footer{padding:14px 16px}
}
@media(max-width:1024px){
.admin-modal .admin-fieldset.cols-2{grid-template-columns:1fr}
}

/* ============================================================ ADMIN — Inline promote toggle (lead-edit drawer) ============================================================ */ .admin-toggle-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:600}
.admin-toggle-label input[type=checkbox]{width:36px;height:20px;accent-color:var(--admin-accent);cursor:pointer}

/* ============================================================ ADMIN — Compact status dot (Contacted / Consult / Downloaded) ============================================================ */ .admin-status-dot{
  display:inline-flex;align-items:center;justify-content:center;
  width:24px;height:24px;border-radius:50%;
  font-size:.8rem;font-weight:700;line-height:1;
}
.admin-status-dot--green{background:rgba(34,197,94,.12);color:#16a34a}
.admin-status-dot--muted{background:transparent;color:var(--admin-text-muted);font-weight:400}
.admin-status-dot--amber{background:rgba(251,191,36,.12);color:#f59e0b}

/* ============================================================ ADMIN — Icon-only edit trigger (replaces full "Edit" button) ============================================================ */ .admin-icon-edit{
  display:inline-flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:8px;
  background:none;border:1px solid var(--admin-border);
  font-size:.95rem;cursor:pointer;
  transition:background .15s,border-color .15s,transform .1s;
  line-height:1;
}
.admin-icon-edit:hover{background:var(--admin-bg,#f1f5f9);border-color:var(--admin-accent-text)}
.admin-icon-edit:active{transform:scale(.88);background:var(--admin-bg,#f1f5f9)}

/* ============================================================ ADMIN — Notes quick-peek badge (data grid tooltip) ============================================================ */ .admin-note-badge{
  display:inline-flex;align-items:center;gap:3px;
  font-size:.68rem;font-weight:600;
  color:var(--admin-text-muted);
  background:var(--admin-bg,#f8fafc);
  border:1px solid var(--admin-border);
  border-radius:20px;padding:2px 7px;
  cursor:default;white-space:nowrap;
  margin-top:5px;
}

/* ============================================================ ADMIN — Live duplicate-lead warning badge ============================================================ */ .admin-dup-warning{
  margin-top:6px;padding:6px 10px;
  font-size:.74rem;font-weight:600;
  color:var(--admin-danger,#dc2626);
  background:var(--admin-danger-bg,#fef2f2);
  border:1px solid var(--admin-danger,#dc2626);
  border-radius:6px;
}


/* ============================================================ ADMIN — Responsiveness sweep: leads tabs,drawers,timeline,touch targets ============================================================ */ /* Timeline indent: slightly wider rail on desktop for a spacious feel */ .admin-note-entry{padding-left:18px}

@media(max-width:1023px){
.admin-table th,.admin-table td{padding:10px 12px}
}

@media(max-width:767px){
/* 1. Tab nav: scroll horizontally instead of wrapping/overlapping */ .admin-tabs-nav{
    flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;
    scrollbar-width:none;
  }
  .admin-tabs-nav::-webkit-scrollbar{display:none}
  .admin-tabs-nav button{white-space:nowrap;flex-shrink:0}

  /* Table density: tighter padding,no accidental truncation of names/numbers */ .admin-table th,.admin-table td{padding:8px 10px;font-size:.8rem}
  /* Cells may wrap -- a headline over three lines is fine on a phone. The
     exclusion is for the single-token elements, which are re-fixed to nowrap
     further down; a badge told it may wrap is how "Active" broke in two. */
  .admin-table td div,.admin-table td span{white-space:normal}

  /* 2. Quick-add inline row (input + checkmark button) stacks on mobile */ #add-service-inline > div{flex-direction:column}
  #add-service-inline input,#add-service-inline button{width:100%}

  /* 3. Notes timeline: tighter indent,no side-by-side meta/text (already stacked) */ .admin-note-entry{padding-left:10px}
  .admin-note-meta{font-size:.68rem}

  /* 4. Touch targets: Cancel/Create/Save/Close buttons at least 48px tall */ .ideas-footer .admin-btn,.admin-drawer-footer .admin-btn{
    min-height:48px;
  }
}


/* ============================================================
   ADMIN — Shared zone-editor pattern (full-page forms with a
   compact meta bar + bounded content editor + centered footer).
   Originally built for Create Idea; now reused by Blog Posts too. ============================================================ */ .ideas-editor{
  display:flex;flex-direction:column;
  height:auto;
  background:var(--admin-card-bg);
  border:1px solid var(--admin-border);
  border-radius:16px;
  overflow:hidden;
  box-shadow:var(--admin-shadow-card);
}
.ideas-meta{
  display:flex;align-items:center;gap:10px;
  padding:0 20px;height:52px;
  border-bottom:1px solid var(--admin-border);
  flex-shrink:0;background:var(--admin-card-bg);
}
.ideas-meta-field{position:relative;display:flex;flex-direction:column;justify-content:center}
.ideas-input{
  height:34px;padding:0 10px;
  border:1px solid var(--admin-border);
  border-radius:8px;
  font-size:.82rem;font-family:inherit;
  color:var(--admin-text);
  background:var(--admin-input-bg);
  outline:none;transition:border-color .15s;
}
.ideas-input:focus{border-color:var(--admin-accent-text)}
.ideas-input-title{width:100%;font-weight:500}
.ideas-toggle{display:flex;align-items:center;gap:6px;cursor:pointer;flex-shrink:0;user-select:none}
.ideas-toggle input{position:absolute;opacity:0;width:0;height:0}
.ideas-toggle-track{
  width:32px;height:18px;border-radius:9px;
  background:var(--admin-border);
  position:relative;transition:background .2s;flex-shrink:0;
}
.ideas-toggle input:checked ~ .ideas-toggle-track{background:var(--admin-accent)}
.ideas-toggle-thumb{
  position:absolute;top:2px;left:2px;
  width:14px;height:14px;border-radius:50%;
  background:#fff;transition:left .2s;
  box-shadow:0 1px 3px rgba(0,0,0,.2);
}
.ideas-toggle input:checked ~ .ideas-toggle-track .ideas-toggle-thumb{left:16px}
.ideas-toggle-label{font-size:.78rem;color:var(--admin-text-muted);white-space:nowrap}
.ideas-url-chip{
  flex-shrink:0;font-size:.72rem;font-family:monospace;
  color:var(--admin-accent-text);text-decoration:none;
  padding:3px 8px;border:1px solid var(--admin-border);
  border-radius:6px;background:var(--admin-btn-secondary-bg);
  white-space:nowrap;transition:border-color .15s;
}
.ideas-url-chip:hover{border-color:var(--admin-accent-text)}
.ideas-desc-row{
  padding:14px 20px;
  border-bottom:1px solid var(--admin-border);
  flex-shrink:0;background:var(--admin-card-bg);
}
.ideas-input-desc{
  width:100%;height:auto;padding:8px 10px;
  resize:none;line-height:1.45;
}
.ideas-template-wrap{
  height:min(42vh, 380px);
  position:relative;
  background:var(--admin-sidebar-bg);
  flex-shrink:0;
}
.ideas-template{
  width:100%;height:100%;
  padding:14px 20px;
  border:none;outline:none;resize:none;
  font-family:"SFMono-Regular",Consolas,"Liberation Mono",Menlo,monospace;
  font-size:.82rem;line-height:1.55;
  color:var(--admin-text);
  background:var(--admin-input-bg);
  display:block;
}
.ideas-footer{
  display:flex;gap:10px;justify-content:center;
  padding:14px 20px;
  border-top:1px solid var(--admin-border);
  flex-shrink:0;background:var(--admin-card-bg);
}
.ideas-err{font-size:.72rem;color:var(--admin-danger,#dc2626);margin-top:2px}

@media(max-width:767px){
.ideas-editor{border-radius:12px}
  .ideas-meta{flex-wrap:wrap;height:auto;padding:12px 16px;gap:8px}
  .ideas-template-wrap{height:auto;min-height:220px}
  .ideas-footer{flex-direction:column-reverse}
  .ideas-footer .admin-btn{width:100%;justify-content:center}
}


/* ════════════════════════════════════════════════════════════════════════ DESIGN SYSTEM V3 — PUBLIC VISUAL LANGUAGE Source of truth: design-system-v3.html Added: Workstream A,Phase 1 (branch redesign/workstream-a) STRATEGY — additive override: This block is appended AFTER all legacy rules,so CSS source order makes it authoritative without rewriting any existing byte. Deleting this block restores the previous visual system exactly. SCOPING RULE — read before editing: Tokens are global. Components that COLLIDE with legacy classes and would break un-migrated dark pages (.card,.glass,.industry-tile,.surface-card) are scoped to .light-section / .dark-section. Pages opt in by wrapping content in a section,which is the design's own architecture. Components that are safe to change globally (buttons,gradient text,eyebrow,tags,badges,stats,fields) are redefined at global scope. ADMIN IS OUT OF SCOPE. The .admin-* rules in design-system-v3.html are deliberately NOT imported. Admin keeps its own --admin-* token namespace and ThemeService-driven palette. ════════════════════════════════════════════════════════════════════════ */ /* ── §1 PRIMITIVES (additive — no legacy primitive is overwritten) ── */ :root{
  /* Orange accent — replaces violet as the public brand colour */
  --c-orange-500:#F98603;
  --c-orange-glow:rgba(249,134,3,.4);
  --c-orange-soft:rgba(249,134,3,.1);
  --c-orange-deep:rgba(249,134,3,.25);
  --c-orange-grad-end:#ff6b35;   /* button gradient terminus */
  --c-coral-end:#ff6b6b;         /* text gradient terminus */

  /* Neutral surfaces */
  --c-black:#060608;
  --c-black-soft:#0a0a0d;

  /* Gray scale (design) */
  --c-gray-50:#f9fafb;  --c-gray-100:#f3f4f6; --c-gray-200:#e5e7eb;
  --c-gray-300:#d1d5db; --c-gray-400:#9ca3af; --c-gray-600:#4b5563;
  --c-gray-700:#374151; --c-gray-800:#1f2937; --c-gray-900:#111827;

  /* 8pt spacing scale — verified NOT to collide with --sp-* / --space-xs..4xl */
  --space-1:8px;   --space-2:16px; --space-3:24px; --space-4:32px;
  --space-5:48px;  --space-6:64px; --space-7:96px; --space-8:120px;

  /* Depth system — all new */
  --depth-1:0 1px 3px rgba(0,0,0,.08);
  --depth-2:0 6px 14px rgba(0,0,0,.10);
  --depth-3:0 14px 28px rgba(0,0,0,.14);
  --depth-4:0 26px 48px rgba(0,0,0,.18);
  --depth-5:0 45px 90px rgba(0,0,0,.24);
  --depth-glass:0 30px 70px -10px rgba(0,0,0,.55);
  --depth-neu:5px 5px 14px rgba(0,0,0,.08),-5px -5px 14px rgba(255,255,255,.95);
  --depth-neu-hover:9px 9px 22px rgba(0,0,0,.12),-9px -9px 22px rgba(255,255,255,1);

  /* Transitions */
  --transition:all .3s cubic-bezier(.4,0,.2,1);
  --transition-fast:all .2s cubic-bezier(.4,0,.2,1);
}

/* ── §2 SEMANTIC CONTRACT — re-point brand roles to orange ── */ :root{
  --color-brand:        var(--c-orange-500);
  --color-brand-glow:   var(--c-orange-glow);
  --color-brand-tint:   var(--c-orange-soft);
  --color-brand-line:   var(--c-orange-deep);
  --color-brand-deep:   var(--c-orange-deep);

  /* Section surface roles (the design's light/dark rhythm) */
  --color-ink:          var(--c-black);
  --color-ink-2:        var(--c-black-soft);
  --color-paper:        #ffffff;

  /* Radius — design scale (button is a 12px rect, NOT a pill) */
  --radius-sm:  8px;
  --radius-md:  12px;
  --radius-lg:  16px;
  --radius-xl:  24px;
  --radius-2xl: 32px;
  --radius-button: var(--radius-md);
  --radius-card:   var(--radius-lg);
  --radius-input:  var(--radius-md);
  --radius-banner: var(--radius-2xl);

  /* Brand gradients — orange */
  --gradient-brand:  linear-gradient(135deg,var(--c-orange-500) 0%,var(--c-orange-grad-end) 100%);
  --gradient-phrase: linear-gradient(135deg,var(--c-orange-500) 0%,var(--c-coral-end) 100%);
  --gradient-brand-soft: linear-gradient(135deg,var(--c-orange-soft),rgba(255,107,53,.08));

  /* Legacy alias re-point so un-migrated pages lose violet immediately */
  --violet: var(--c-orange-500);
  --grad:   var(--gradient-brand);
  --shadow-pop: 0 10px 28px rgba(249,134,3,.38);
  --shadow-gradient-hover: 0 16px 36px rgba(249,134,3,.5);
  --glow-line: var(--c-orange-deep);
  --shadow-glow: 0 0 60px rgba(249,134,3,.28);
  --shadow-brand-mark: 0 6px 16px rgba(249,134,3,.32);
  --color-brand-2-tint: var(--c-orange-soft);
  --color-brand-2-line: var(--c-orange-deep);
}

/* ── §3 LIGHT / DARK SECTION SYSTEM ── The defining rhythm of the design: light tactile surfaces alternating with dark dimensional glass. Sections carry their own surface + text colour so components inside them resolve correctly. */ .section{padding:var(--space-8) var(--space-4);position:relative}
.section-sm{padding:var(--space-6) var(--space-4);position:relative}

.light-section{background:var(--color-paper);color:var(--c-gray-900)}


.dark-section{
  /* design-system-v3: a three-stop with a warm middle, not a flat fill.
     The flat version is what made stacked dark sections read as one block.

     Held at .86 so the shared backdrop reads through it. Dark over dark:
     the surface barely moves and text contrast is unchanged. Light
     sections stay fully opaque on purpose -- a translucent white would
     stop being white and would shift tint as blobs drifted beneath it,
     which is the failure the surface/text contract exists to stop. */
  /* .72, not .86. The backdrop underneath is the same dark gradient, so
     letting more of it through does not lighten the section -- the only
     thing it changes is how much of the blobs survive. At .86 a blob was
     arriving at 14 per cent strength and could not be seen at all. */
  background:linear-gradient(165deg,rgba(8,8,10,.72) 0%,rgba(13,9,6,.72) 55%,rgba(6,6,8,.72) 100%);
  color:#fff;position:relative;overflow:hidden}
.dark-section::before{
  content:'';position:absolute;inset:0;pointer-events:none;z-index:0;
  background:
    radial-gradient(circle at 20% 50%,rgba(249,134,3,.08) 0%,transparent 50%),
    radial-gradient(circle at 80% 80%,rgba(249,134,3,.05) 0%,transparent 50%);
}
.dark-section > *{position:relative;z-index:1}

/* seam between stacked sections so boundaries read clearly */ .section + .section{box-shadow:inset 0 1px 0 rgba(127,127,127,.08)}

/* .section-head is defined once, in the detail-page template block further
   down, which sets the same margin and alignment plus the measure. This
   earlier copy never applied. */
.section-eyebrow{
  display:inline-block;background:var(--color-brand-tint);color:var(--color-brand);
  padding:6px 18px;border-radius:24px;font-size:11px;font-weight:700;
  letter-spacing:1px;text-transform:uppercase;margin-bottom:var(--space-2);
}
.section-desc{color:var(--c-gray-600);font-size:17px;max-width:620px;margin:0 auto;font-weight:400}
.dark-section .section-desc{color:var(--c-gray-400)}

/* ── §4 TYPOGRAPHY ──
   Design sizes are the desktop anchor; clamp() lands on the design's own 768px values at mobile (see conflict C4 in the implementation status doc). */ .eyebrow{
  font-size:11px;font-weight:700;letter-spacing:2.5px;text-transform:uppercase;
  color:var(--color-brand);margin-bottom:var(--space-2);display:block;
}
.eyebrow::before{content:none}          /* design has no leading rule */ .eyebrow.on-dark{color:var(--color-brand)}

.h-xl{font-size:clamp(40px,5.6vw,56px);font-weight:700;line-height:1.1;letter-spacing:-1px;margin-bottom:var(--space-2)}
.h-lg{font-size:clamp(32px,4.2vw,42px);font-weight:700;line-height:1.2;letter-spacing:-.5px;margin-bottom:var(--space-2)}
.h-md{font-size:clamp(24px,3.2vw,32px);font-weight:600;line-height:1.25;margin-bottom:var(--space-1)}
.h-section{font-size:clamp(28px,4vw,40px);font-weight:700;line-height:1.2;letter-spacing:-.5px;margin-bottom:var(--space-2)}

.serif-accent{font-family:var(--font-heading);font-style:italic;color:var(--color-brand);font-weight:400}
.dark-section .serif-accent{color:var(--color-brand)}

/* gradient text — orange→coral,still honours the fluid-gradient params */ .grad-text{
  background:var(--gradient-phrase);
  background-size:var(--gf-stretch,260% 100%);
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;
  animation:gradFlow var(--gf-dur-text,6s) var(--gf-ease,linear) infinite;
}

.lead{font-size:18px;line-height:1.7;color:var(--c-gray-600);font-weight:400}
.dark-section .lead,.lead.on-dark{color:var(--c-gray-300)}
.body{font-size:16px;line-height:1.6;color:var(--c-gray-700)}
.dark-section .body{color:var(--c-gray-400)}
.prose{max-width:65ch;font-size:16px;line-height:1.8;color:var(--c-gray-700)}
.prose p{margin-bottom:var(--space-2)}
.prose strong{color:var(--c-gray-900);font-weight:600}
.prose em{color:var(--c-gray-600)}
.dark-section .prose{color:var(--c-gray-300)}
.dark-section .prose strong{color:#fff}

/* ── §5 BUTTONS (global — safe,colour/shape only) ── */ .btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 28px;border-radius:var(--radius-md);font-size:14px;font-weight:600;
  cursor:pointer;transition:var(--transition-fast);border:none;text-decoration:none;
  line-height:1;white-space:nowrap;
}
.btn-grad{
  background:var(--gradient-brand);color:#fff;
  box-shadow:0 10px 28px rgba(249,134,3,.38);
  /* design's CTA is a solid gradient, not an animated sweep */
  background-size:auto;animation:none;
}
.btn-grad:hover{transform:translateY(-3px);box-shadow:0 16px 36px rgba(249,134,3,.5)}
.btn-grad:active{transform:translateY(0) scale(.97);box-shadow:0 4px 12px rgba(249,134,3,.3)}

.btn-dark{background:var(--color-ink);color:#fff;box-shadow:var(--depth-3)}
.btn-dark:hover{background:var(--c-gray-800);transform:translateY(-3px);box-shadow:var(--depth-4)}

.btn-line{background:transparent;color:var(--c-gray-900);border:2px solid var(--c-gray-300)}
.btn-line:hover{border-color:var(--color-brand);color:var(--color-brand)}
.dark-section .btn-line{color:#fff;border-color:rgba(255,255,255,.28)}
.dark-section .btn-line:hover{border-color:var(--color-brand);color:var(--color-brand)}

.btn-ghost{background:transparent;color:var(--c-gray-600)}
.btn-ghost:hover{background:var(--c-gray-100);color:var(--c-gray-900)}
.dark-section .btn-ghost{color:var(--c-gray-300)}
.dark-section .btn-ghost:hover{background:rgba(255,255,255,.08);color:#fff}

.btn-white{background:#fff;color:var(--color-ink);box-shadow:var(--depth-4)}
.btn-white:hover{transform:translateY(-3px);box-shadow:var(--depth-5)}

.btn svg{width:16px;height:16px;transition:transform var(--dur-base,.25s)}
.btn:hover svg{transform:translateX(3px)}

/* ── §6 CARDS & SURFACES (section-scoped — protects un-migrated pages) ── */ .light-section .card{
  background:#fff;border-radius:var(--radius-lg);padding:var(--space-4);
  box-shadow:var(--depth-neu);transition:var(--transition);transform-style:preserve-3d;
  color:var(--c-gray-900);
}
.light-section .card:hover{
  box-shadow:var(--depth-neu-hover);transform:translateY(-8px) translateZ(20px);
}
.dark-section .card{
  background:rgba(10,10,13,.68);border:1px solid rgba(255,255,255,.16);
  border-radius:var(--radius-lg);padding:var(--space-4);color:#fff;
  box-shadow:0 0 0 1px rgba(255,255,255,.08),var(--depth-glass),inset 0 1px 0 0 rgba(255,255,255,.14);
  transition:var(--transition);
}
.dark-section .card:hover{transform:translateY(-8px);box-shadow:0 0 0 1px rgba(255,255,255,.12),var(--depth-glass),0 0 60px rgba(249,134,3,.18)}

.light-section .glass{
  background:rgba(255,255,255,.78);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border:1px solid rgba(255,255,255,.6);border-radius:var(--radius-lg);padding:var(--space-4);
  box-shadow:0 10px 36px rgba(0,0,0,.1);
}
.dark-section .glass{
  background:rgba(10,10,13,.68);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--radius-lg);padding:var(--space-4);
  box-shadow:0 0 0 1px rgba(255,255,255,.08),var(--depth-glass),inset 0 1px 0 0 rgba(255,255,255,.14);
  position:relative;color:#fff;
}
.dark-section .glass::before{
  content:'';position:absolute;inset:0;border-radius:var(--radius-lg);pointer-events:none;
  background:linear-gradient(135deg,rgba(255,255,255,.06) 0%,rgba(255,255,255,0) 50%);
}

.light-section .surface-card{
  background:#fff;border:none;border-radius:var(--radius-xl);padding:var(--space-5);
  box-shadow:var(--depth-neu);transition:var(--transition);
}
.light-section .surface-card:hover{
  box-shadow:var(--depth-neu-hover);transform:translateY(-8px);
}

.card-solid{background:var(--c-gray-900);color:#fff;border-radius:var(--radius-lg);padding:var(--space-4);box-shadow:var(--depth-4)}

.glow-hover{transition:var(--transition);cursor:pointer}
.glow-hover:hover{box-shadow:0 0 60px rgba(249,134,3,.28);transform:translateY(-6px)}

/* industry tile — inverts to black on hover (design behaviour) */ .light-section .industry-tile{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:var(--space-5);border-radius:var(--radius-lg);background:#fff;
  border:1px solid var(--color-border);
  box-shadow:var(--depth-neu);transition:var(--transition),border-color .25s ease;cursor:pointer;
  text-align:center;position:relative;isolation:isolate;color:var(--c-gray-900);
}
.light-section .industry-tile::after{
  content:'';position:absolute;inset:0;border-radius:var(--radius-lg);
  background:var(--color-ink);opacity:0;transition:opacity .3s ease;z-index:-1;
}
.light-section .industry-tile:hover{
  color:#fff;border-color:var(--color-brand);transform:translateY(-8px);box-shadow:var(--depth-neu-hover);
}
.light-section .industry-tile:hover::after{opacity:1}

/*
 * Quiet by default, brand on hover.
 *
 * design-system-v3 draws this tile brand-tinted with a brand glyph, and
 * demonstrates one of them. In a card grid there are six to a section and
 * eighteen on the homepage, at which point the accent has stopped being an
 * accent and become the palette. The colour returns on hover, where it also
 * does some work -- telling you the card is a thing you can go into.
 */
.card-icon{
  width:56px;height:56px;border-radius:var(--radius-md);background:var(--color-surface-hover);
  display:flex;align-items:center;justify-content:center;color:var(--color-text-muted);
  margin-bottom:var(--space-2);font-size:24px;font-weight:700;transition:var(--transition);
}

/* The accent, on the one card being looked at. */
.card:hover .card-icon,
.surface-card:hover .card-icon,
a:hover > .card-icon,
a:hover .card-icon{
  background:var(--color-brand-tint);
  color:var(--color-brand);
}
.card:hover .card-icon{transform:translateZ(30px) scale(1.1) rotateZ(5deg)}
.card-title{font-size:20px;font-weight:700;margin-bottom:var(--space-1);color:var(--c-gray-900)}
.dark-section .card-title{color:#fff}

.chip{
  display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:20px;
  font-size:13px;font-weight:500;background:var(--c-gray-100);color:var(--c-gray-700);
  border:none;transition:var(--transition-fast);cursor:pointer;
}
.chip:hover{background:var(--color-brand-tint);color:var(--color-brand);transform:translateY(-2px);box-shadow:0 6px 16px rgba(249,134,3,.22)}
.dark-section .chip{background:rgba(255,255,255,.08);color:var(--c-gray-300)}
.dark-section .chip:hover{background:var(--color-brand-tint);color:var(--color-brand)}

/* ── §7 CONTENT & MEDIA CARDS (imagery is first-class in this system) ── */ .post-card{background:#fff;border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--depth-3);transition:var(--transition)}
.post-card:hover{box-shadow:var(--depth-5);transform:translateY(-8px)}
.post-thumb{width:100%;height:190px;overflow:hidden;background:var(--gradient-brand-soft);display:grid;place-items:center}
.post-thumb img{width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.post-card:hover .post-thumb img{transform:scale(1.1)}
.post-body{padding:var(--space-3)}
.post-title{font-size:17px;font-weight:700;color:var(--c-gray-900);margin-bottom:6px;line-height:1.3}
.post-meta{display:flex;justify-content:space-between;font-size:13px;color:var(--c-gray-500,#6b7280)}

.team-card{display:flex;align-items:center;gap:var(--space-2);background:#fff;border-radius:var(--radius-lg);padding:var(--space-3);box-shadow:var(--depth-neu);transition:var(--transition)}
.team-card:hover{box-shadow:var(--depth-neu-hover);transform:translateY(-4px)}
.media-avatar{width:64px;height:64px;border-radius:50%;overflow:hidden;box-shadow:var(--depth-2);flex-shrink:0;background:var(--gradient-brand-soft);display:grid;place-items:center;aspect-ratio:auto}
.media-avatar img{width:100%;height:100%;object-fit:cover}
.light-section .team-name{font-size:16px;font-weight:700;color:var(--c-gray-900)}
.light-section .team-role{font-size:13px;color:var(--c-gray-500,#6b7280);margin-top:2px}

.image-card{border-radius:var(--radius-lg);overflow:hidden;background:#fff;box-shadow:var(--depth-3);transition:var(--transition)}
.image-card:hover{transform:translateY(-10px);box-shadow:var(--depth-5)}
.image-card-photo{width:100%;height:220px;overflow:hidden;background:var(--gradient-brand-soft);display:grid;place-items:center}
.image-card-photo img{width:100%;height:100%;object-fit:cover;transition:transform .5s ease}
.image-card:hover .image-card-photo img{transform:scale(1.1)}
.image-card-body{padding:var(--space-3)}
.image-card-title{font-size:16px;font-weight:700;margin-bottom:4px;color:var(--c-gray-900)}
.image-card-meta{font-size:13px;color:var(--c-gray-500,#6b7280)}

/* branded fallback when no image exists — never a stock placeholder */ .img-fallback{
  width:100%;height:100%;display:grid;place-items:center;
  background:var(--gradient-brand-soft);color:var(--color-brand);
  font-size:24px;font-weight:700;letter-spacing:-.5px;
}

.avatar-stack{display:flex;align-items:center;padding-left:var(--space-2)}
.avatar-stack img{width:56px;height:56px;border-radius:50%;object-fit:cover;border:3px solid #fff;box-shadow:var(--depth-2);margin-left:-16px;transition:var(--transition-fast)}
.avatar-stack img:hover{transform:translateY(-6px) scale(1.06);z-index:5;box-shadow:var(--depth-4)}
.avatar-stack-more{width:56px;height:56px;border-radius:50%;background:var(--c-gray-900);color:#fff;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;border:3px solid #fff;margin-left:-16px;box-shadow:var(--depth-2)}

.gallery-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-2)}
.gallery-item{border-radius:var(--radius-md);overflow:hidden;box-shadow:var(--depth-2);transition:var(--transition);cursor:pointer}
.gallery-item:hover{box-shadow:var(--depth-4);transform:translateY(-6px)}
.gallery-item img{width:100%;height:150px;object-fit:cover;transition:transform .5s ease}
.gallery-item:hover img{transform:scale(1.12)}

/* ── §8 STATS,TAGS,BADGES ── */ .stat-num{font-size:52px;font-weight:700;color:var(--color-brand);line-height:1;margin-bottom:var(--space-1);letter-spacing:-1px}
.stat-label{font-size:13px;color:var(--c-gray-500,#6b7280);font-weight:600;text-transform:uppercase;letter-spacing:.5px;margin-top:0}
.dark-section .stat-label{color:var(--c-gray-400)}
.dark-section .stat-num{text-shadow:0 0 34px rgba(249,134,3,.4)}

.tag{display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;background:var(--c-gray-100);color:var(--c-gray-700);border:none;margin:0 4px 4px 0;transition:var(--transition-fast)}
.tag:hover{background:var(--color-brand-tint);color:var(--color-brand)}
.dark-section .tag{background:rgba(255,255,255,.08);color:var(--c-gray-300)}

.badge-featured{display:inline-flex;align-items:center;background:var(--color-brand);color:#fff;padding:5px 14px;border-radius:12px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;border:none;box-shadow:0 6px 16px rgba(249,134,3,.32)}
.badge-active{background:#10b981;color:#fff;padding:4px 12px;border-radius:12px;font-size:10px;font-weight:700;text-transform:uppercase;box-shadow:0 6px 16px rgba(16,185,129,.32)}
.badge-coming-soon{background:var(--c-gray-200);color:var(--c-gray-600);padding:4px 12px;border-radius:12px;font-size:10px;font-weight:700;text-transform:uppercase}
/* was #8b5cf6, a purple from an older palette that the design guide does
   not contain. Follows .badge-featured's pattern on the brand instead. */
.badge-beta{background:var(--color-brand-2);color:#fff;padding:4px 12px;border-radius:12px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;box-shadow:0 6px 16px var(--color-brand-2-line)}

/* ── §9 FORMS ── NOTE: legacy .field is the INPUT itself (contact form uses class="field" on inputs),whereas design-system-v3 uses .field as a flex wrapper + .field-input. Redefining .field as a wrapper would break every existing form,so the legacy semantics are kept and restyled to the design's input spec. .field-input is provided as an alias for new markup. Documented as conflict C6. */ .field,.field-input{
  width:100%;padding:12px 16px;border:2px solid var(--c-gray-200);border-radius:var(--radius-md);
  font-family:inherit;font-size:14px;color:var(--c-gray-900);background:#fff;
  outline:none;transition:var(--transition-fast);box-shadow:var(--depth-1);
}
.field:focus,.field-input:focus{
  border-color:var(--color-brand);
  box-shadow:0 0 0 3px var(--color-brand-tint),var(--depth-2);
  transform:translateY(-1px);
}
.field::placeholder,.field-input::placeholder{color:var(--c-gray-400);opacity:1}
.field-label{display:block;font-size:13px;font-weight:600;color:var(--c-gray-700);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px}
.field-error{display:block;color:#ef4444;font-size:12px;font-weight:500;margin-top:5px}
.alert-ok{background:#10b981;color:#fff;padding:14px 20px;border-radius:var(--radius-md);font-size:14px;font-weight:600;display:flex;align-items:center;gap:8px;border:none;box-shadow:0 8px 22px rgba(16,185,129,.32);margin-bottom:var(--space-3)}
.dark-section .field-label{color:var(--c-gray-300)}

/* ── §10 CTA — four stacked depth layers: photo,gradient,orbs,glass ── Reuses the existing .cta-orb animation contract (incl. .is-paused off-screen pause driven by scroll-animations.js) rather than introducing a second system. */ .cta-banner{
  position:relative;overflow:hidden;max-width:1240px;margin:0 auto;text-align:center;
  border-radius:var(--radius-2xl);padding:var(--space-8) var(--space-5);
  color:#fff;
  /* Four-layer stack per the design: photo, gradient, orbs, glass.
     --cta-image cascades entity hero -> global setting -> bundled texture. */
  background:linear-gradient(180deg,rgba(6,6,8,.68) 0%,rgba(6,6,8,.92) 100%),
             var(--cta-image,url('/images/texture/texture-deep.jpg')) center/cover,
             var(--color-ink);
  border:none;
  box-shadow:var(--depth-glass);
}
/* opt-in photographic layer — set --cta-image on the element when a real asset exists */ .cta-banner .cta-body{position:relative;z-index:2}
/* .cta-body is the content wrapper used by partials/blocks/cta.blade.php,which layers .cta-glass as a decorative sheet rather than a card. */ .cta-banner.has-image{
  background:linear-gradient(180deg,rgba(6,6,8,.68) 0%,rgba(6,6,8,.92) 100%),var(--cta-image) center/cover;
}
.cta-orb{
  position:absolute;border-radius:50%;pointer-events:none;z-index:0;
  background:radial-gradient(circle at 30% 30%,var(--color-brand-glow) 0%,rgba(249,134,3,.15) 40%,transparent 70%);
  animation:float-3d 8s ease-in-out infinite;
  width:320px;height:320px;top:-120px;left:-120px;filter:blur(40px);opacity:1;
}
.cta-orb.orb-2{width:280px;height:280px;top:auto;left:auto;bottom:-100px;right:-100px;animation-delay:-3s;filter:blur(35px)}
@keyframes float-3d{
0%,100%{transform:translate3d(0,0,0) scale(1);opacity:.6}
  25%{transform:translate3d(20px,-25px,30px) scale(1.05);opacity:.7}
  50%{transform:translate3d(-10px,-35px,60px) scale(1.1);opacity:.8}
  75%{transform:translate3d(-20px,-15px,30px) scale(1.05);opacity:.7}
}
.cta-banner.is-paused .cta-orb{animation-play-state:paused}
.cta-glass{
  position:relative;z-index:2;inset:auto;border-radius:var(--radius-lg);padding:var(--space-5);
  background:rgba(6,6,8,.55);backdrop-filter:blur(30px);-webkit-backdrop-filter:blur(30px);
  border:1px solid rgba(255,255,255,.18);
  box-shadow:0 0 0 1px rgba(255,255,255,.1),inset 0 1px 0 0 rgba(255,255,255,.1);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
.cta-glass{background:rgba(6,6,8,.9)}
}

/* ── §11 GRID ── */ .bento-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--space-4);max-width:1240px;margin:0 auto}
.col-3{grid-column:span 3}
.col-4{grid-column:span 4}
.col-6{grid-column:span 6}
.col-8{grid-column:span 8}
.col-12{grid-column:span 12}
.content-card-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-4)}

/* ── §12 RESPONSIVE LADDER (mobile-first target from the Workstream A plan) ── */
@media(max-width:1023px){
/* design-system-v3 §21 sets three rungs for section padding -- 120 desktop,
   96 tablet, 64 mobile. The middle one was missing, so everything from 768
   to 1023 was carrying the full desktop 120. */
.section{padding:var(--space-7) var(--space-3)}
  .section-sm{padding:var(--space-6) var(--space-3)}
  .content-card-grid{grid-template-columns:repeat(2,1fr)}
  .gallery-grid{grid-template-columns:repeat(3,1fr)}
}
@media(max-width:767px){
.section{padding:var(--space-6) var(--space-3)}
  .section-sm{padding:var(--space-5) var(--space-3)}
  .section-head{margin-bottom:var(--space-4)}
  .col-3,.col-4,.col-6,.col-8{grid-column:span 12}
  .content-card-grid{grid-template-columns:1fr}
  .gallery-grid{grid-template-columns:repeat(2,1fr)}
  .cta-banner{padding:var(--space-6) var(--space-3)}
  .cta-orb{display:none}
  .stat-num{font-size:40px}
  .btn{width:auto}
}

/* ── §13 ACCESSIBILITY ── */ :where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:3px solid var(--color-brand);
  outline-offset:2px;
  border-radius:var(--radius-sm);
}
@media(prefers-reduced-motion:reduce){
.cta-orb{animation:none}
  .post-card:hover .post-thumb img,.image-card:hover .image-card-photo img,.gallery-item:hover img{transform:none}
  .card:hover,.surface-card:hover,.industry-tile:hover,.post-card:hover,.image-card:hover,.team-card:hover,.btn-grad:hover,.btn-dark:hover,.btn-white:hover{transform:none}
}
/* end DESIGN SYSTEM V3 */ /* ── §14 THEME-SERVICE ESCALATION ───────────────────────────────────────── ThemeService injects `<style>:root{…}</style>` INLINE in the layout, AFTER
   the app.css <link>. Inline source order therefore beats this stylesheet for
   any token the active DB palette defines.

   The active palette ("Sunset") already uses the design's orange #F98603, but
   pairs it with a navy surface (#010B13), ivory text (#FFFFF0), a crimson
   secondary (#C50022) and a 5-stop brand gradient — none of which exist in
   design-system-v3.html. Per the source-priority rule the design file wins.

   Rather than mutate the DB palette or ThemeService (explicitly protected),
   the design's BRAND tokens are re-declared at `:root:root` (specificity
   0,2,0 > 0,1,0) so they win regardless of source order.

   DELIBERATELY NOT ESCALATED: --color-surface, --color-text* and friends.
   Un-migrated pages still hardcode `background:var(--ink)` and expect a dark
   base; flipping those globally would render white-on-white until every page is migrated (conflict C1). Sections opt into the new light/dark system via .light-section / .dark-section,which set their own surface + text. Admin is untouched: it consumes the --admin-* namespace,which ThemeService still owns end-to-end. ───────────────────────────────────────────────────────────────────────── */ /* Plain :root on purpose. These sit after the older palettes they override,so source order is enough - and it lets an uploaded ThemePalette (injected inline as :root) win,which a doubled selector prevented. */ :root{
  --color-brand:         #F98603;
  --color-brand-glow:    rgba(249,134,3,.4);
  --color-brand-tint:    rgba(249,134,3,.1);
  --color-brand-line:    rgba(249,134,3,.25);
  --color-brand-deep:    rgba(249,134,3,.25);

  --gradient-brand:      linear-gradient(135deg,#F98603 0%,#ff6b35 100%);
  --gradient-phrase:     linear-gradient(135deg,#F98603 0%,#ff6b6b 100%);
  --gradient-brand-soft: linear-gradient(135deg,rgba(249,134,3,.1),rgba(255,107,53,.08));

  --shadow-pop:            0 10px 28px rgba(249,134,3,.38);
  --shadow-gradient-hover: 0 16px 36px rgba(249,134,3,.5);
  --shadow-brand-mark:     0 6px 16px rgba(249,134,3,.32);
  --glow-line:             rgba(249,134,3,.25);
  --shadow-glow:           0 0 60px rgba(249,134,3,.28);

  /* legacy aliases — kill violet/cyan leakage on un-migrated pages */
  --violet:              #F98603;
  --cyan:                #F98603;
  --grad:                linear-gradient(135deg,#F98603 0%,#ff6b35 100%);
  --color-brand-2:       #F98603;
  --color-brand-2-tint:  rgba(249,134,3,.1);
  --color-brand-2-line:  rgba(249,134,3,.25);
  --blob-color-1:        #F98603;
  --blob-color-2:        #ff6b35;
  --blob-color-3:        #ff6b6b;

  /* radius — design scale */
  --radius-sm:8px; --radius-md:12px; --radius-lg:16px;
  --radius-xl:24px; --radius-2xl:32px;
  --radius-button:12px; --radius-card:16px; --radius-input:12px; --radius-banner:32px;
}
/* end THEME-SERVICE ESCALATION */

/* ── §15 DARK-BASE SAFETY CORRECTION ──────────────────────────────────────
   Found during Phase 1 verification: .btn-ghost rendered --c-gray-600 on the
   still-dark homepage hero (2.65:1 — below WCAG AA).

   Cause: §5–§9 applied design-system-v3's LIGHT-section text colours at global
   scope. But per conflict C1 the base surface stays dark until each page is
   migrated, so unscoped defaults land on black.

   Correction: unscoped defaults are dark-safe; the design's light values are
   scoped to .light-section / .light-section-alt. Once a page is migrated into
   sections it gets the design values exactly; until then it stays legible. This block must remain AFTER §5–§9. ───────────────────────────────────────────────────────────────────────── */ /* text roles — dark-safe by default */ .lead{color:var(--c-gray-300)}
.body{color:var(--c-gray-400)}
.prose{color:var(--c-gray-300)}
.prose strong{color:#fff}
.prose em{color:var(--c-gray-400)}
.field-label{color:var(--c-gray-300)}
.stat-label{color:var(--c-gray-400)}
.post-meta,.image-card-meta,.team-role{color:var(--c-gray-400)}

/* …and the design's light values where a section opts in */ .light-section .lead{color:var(--c-gray-600)}
.light-section .body{color:var(--c-gray-700)}
.light-section .prose{color:var(--c-gray-700)}
.light-section .prose strong .prose strong{color:var(--c-gray-900)}
.light-section .prose em .prose em{color:var(--c-gray-600)}
.light-section .field-label{color:var(--c-gray-700)}
.light-section .stat-label{color:#6b7280}
.light-section .post-meta,.light-section .image-card-meta,.light-section .team-role{color:#6b7280}

/* card/post/image titles live on white cards,which exist in BOTH section tones (a white .card inside .dark-section is intentional in the design),so these stay dark and are only lightened when the card itself is glass */ .dark-section .glass .card-title,.dark-section .glass .post-title,.dark-section .glass .image-card-title{color:#fff}

/* buttons — dark-safe defaults */ .btn-ghost{color:var(--c-gray-300)}
.btn-ghost:hover{background:rgba(255,255,255,.08);color:#fff}
.light-section .btn-ghost{color:var(--c-gray-600)}
.light-section .btn-ghost:hover{background:var(--c-gray-100);color:var(--c-gray-900)}

.btn-line{color:#fff;border:2px solid rgba(255,255,255,.28)}
.btn-line:hover{border-color:var(--color-brand);color:var(--color-brand)}
.light-section .btn-line{color:var(--c-gray-900);border-color:var(--c-gray-300)}
.light-section .btn-line:hover{border-color:var(--color-brand);color:var(--color-brand)}

/* chips/tags — dark-safe defaults */ .chip{background:rgba(255,255,255,.08);color:var(--c-gray-300)}
.chip:hover{background:var(--color-brand-tint);color:var(--color-brand)}
.light-section .chip{background:var(--c-gray-100);color:var(--c-gray-700)}
.tag{background:rgba(255,255,255,.08);color:var(--c-gray-300)}
.light-section .tag{background:var(--c-gray-100);color:var(--c-gray-700)}

/* eyebrow stays brand-orange in both tones (design behaviour) */ .eyebrow,.eyebrow.on-dark{color:var(--color-brand)}
/* end DARK-BASE SAFETY CORRECTION */ /* ── §16 TYPEFACES — design-system-v3 literal stacks ────────────────────── Decision (user,2026-08-22): use the design file's stacks verbatim rather than Prabhabi's previously-loaded Inter + Instrument Serif. Resolves conflict C2 in favour of the design file (source priority 1). Re-pointing the two PRIMITIVE stacks cascades through every downstream alias already defined in this stylesheet: --font-serif-stack -> --font-heading -> --font-serif / --serif --font-sans-stack -> --font-body -> --font-sans / --sans Consequence: the public site no longer depends on the Google Fonts request for Inter/Instrument Serif. Admin is unaffected — it sets 'DM Sans' directly on .admin-wrapper and loads its own font link. ───────────────────────────────────────────────────────────────────────── */ :root{
  --font-sans-stack:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --font-serif-stack:Georgia,'Times New Roman',serif;

  --font-sans:var(--font-sans-stack);
  --font-serif:var(--font-serif-stack);
  --font-body:var(--font-sans-stack);
  --font-heading:var(--font-serif-stack);
  --sans:var(--font-sans-stack);
  --serif:var(--font-serif-stack);
}

/* design body defaults */ body{font-family:var(--font-sans-stack);line-height:1.6}

/* ── §17 ACCEPTED ACCESSIBILITY DEVIATION ──────────────────────────────── Decision (user,2026-08-22): implement design-system-v3 exactly and accept the WCAG AA failure for white text on the brand orange. Measured: white on #F98603 = 2.49:1 (gradient end #ff6b35 = 2.84:1). AA requires 4.5:1 small / 3.0:1 large — this fails both. Near-black #060608 on the same orange would score 8.14:1. Affects: .btn-grad (primary CTA) and .badge-featured. REVERSED (user, 2026-08-27) for the CTA only: .btn-grad and .nav-cta are now near-black on the orange. .badge-featured is untouched -- it had already been moved to the darker --color-brand-strong where white passes, and near-black there measures 4.03:1, which is worse. Measured live in both themes on four page types: 8.14:1 at the gradient start, 7.14:1 at the end, against the 4.5:1 this text size needs. The rule is at the end of this file under "§17 REVERSED"; it is kept there rather than edited into §5 and §8 so the original design values stay visible and the reversal stays one block that can be lifted out.
   ───────────────────────────────────────────────────────────────────────── */ /* end TYPEFACES + ACCEPTED DEVIATION */ /* ── §18 COMPONENT SUPPORT CLASSES ──────────────────────────────────────── Classes referenced by the Blade component layer (resources/views/components) that have no equivalent in design-system-v3.html because that file is a static demo with no component API. ───────────────────────────────────────────────────────────────────────── */ /* <x-icon> — inherits colour,aligns with adjacent text */ .icon{display:inline-block;vertical-align:middle;flex-shrink:0;color:inherit}


/* <x-card href="…"> — whole-surface link. Keeps the card's own hover/depth transition (defined per tone in section 6) while removing link chrome and preserving a visible keyboard focus ring. */ .card-link{display:block;text-decoration:none;color:inherit}
.card-link:hover{text-decoration:none}
.card-link:focus-visible{outline:3px solid var(--color-brand);outline-offset:3px}

/* Utility: pair an icon with a label without extra wrappers */ .with-icon{display:inline-flex;align-items:center;gap:8px}

/* Media fallback wrapper — used where a real asset does not exist yet. Renders the brand mark on a soft brand wash instead of a stock photo. See conflict C3: the site currently has zero content imagery. */ .media-frame{position:relative;overflow:hidden;background:var(--gradient-brand-soft);display:grid;place-items:center}
.media-frame > img{width:100%;height:100%;object-fit:cover}
/* end COMPONENT SUPPORT CLASSES */ /* ════════════════════════════════════════════════════════════════════════ PUBLIC CHROME + EFFECTS + PAGE STYLES Migrated verbatim out of the inline <style> block in resources/views/layouts/app.blade.php (Workstream A,Phase 3). This completes the consolidation the codebase itself flagged as pending ("stays in the inline block until Phase 5 lock-down"). Contents: nav,mobile overlay,grain,mesh/blobs,grid-lines,marquee,dashboard mock,footer,word-split,card tilt/magnetic,orbital ecosystem,hero phrase,mobile product strip,keyword highlight,submit button,plus page-scoped rules for Contact,Feature Grid,Blog,About,Product Detail and Solutions. Appended LAST on purpose: as an inline block this CSS overrode everything in app.css,including the design-system block. Appending it at the end reproduces that precedence exactly,so the migration is visually inert. Source hash (of the original inline block): d31a55186bc6 ════════════════════════════════════════════════════════════════════════ */ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* legacy tokens moved to css/app.css LEGACY BRIDGE (Phase 2A) */ html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{font-family:var(--sans);background:var(--color-ink);color:var(--color-text);line-height:1.5;-webkit-font-smoothing:antialiased;overflow-x:hidden;font-weight:400}
::selection{background:var(--violet);color:var(--color-text)}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}

/* grain overlay */ body::before{content:'';position:fixed;inset:0;z-index:9999;pointer-events:none;opacity:.025;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}

/* ── NAV ── */ .nav{position:fixed;top:0;left:0;right:0;z-index:100;display:flex;align-items:center;justify-content:space-between;
  padding:22px 40px;transition:all .4s cubic-bezier(.4,0,.2,1)}
.nav.scrolled{padding:14px 40px;background:var(--color-nav-scrolled-bg);backdrop-filter:blur(20px) saturate(180%);border-bottom:1px solid var(--line)}
.nav.on-dark:not(.scrolled){color:var(--color-text)}
.nav.on-dark:not(.scrolled) .nav-links a{color:var(--color-text-dim)}
/* The nav CTA used to be an outline button on the dark hero: a border, the
   text colour, and a hover that filled it with that colour and flipped the
   label to the surface colour. It has been a filled gradient button since,
   with near-black text forced by the contrast reversal in §17, and these two
   rules were never taken out with the design they belonged to.

   The hover was the visible fault. background:var(--color-text) is #0f0f17
   at :root, and the label cannot go back to white because §17 sets it with
   !important -- so hovering the primary call to action on every dark hero
   turned it near-black on near-black, legible only by the orange glow the
   shadow left behind. Both are gone; the hover is the lift and the stronger
   shadow below, which is what a filled button should do. */
/* The two rules that were here set the hamburger's bars to --color-text and
   its border to --color-border on the dark hero. Neither token is redefined
   for the nav -- it floats outside the sections that redefine them -- so both
   resolved to the :root light-theme values and the bars came out at #0f0f17
   on a near-black hero. Same fault as the nav CTA had.

   The base rule already draws white bars on a translucent white border,
   which is what the dark hero wants; .nav.scrolled overrides both for the
   light bar. Neither needed help. */
.brand{display:flex;align-items:center;gap:10px;font-weight:600;font-size:1.15rem;letter-spacing:-.02em}
.brand-mark{width:30px;height:30px;border-radius:9px;background:var(--grad);display:grid;place-items:center;
  box-shadow:var(--shadow-brand-mark,0 4px 14px rgba(109,94,252,.4));flex-shrink:0}
.brand-mark svg{width:16px;height:16px}
.nav-links{display:flex;gap:34px;list-style:none}
.nav-links a{font-size:.9rem;font-weight:450;opacity:.7;transition:opacity .2s;position:relative}
.nav-links a:hover{opacity:1}
.nav-right{display:flex;align-items:center;gap:16px}
.nav-cta{font-size:.85rem;font-weight:500;padding:9px 18px;border:1px solid var(--ink);border-radius:100px;transition:all .25s}
/* Same leftover on the light nav. --ink is aliased to --color-surface and
   resolves to #ffffff, so this turned the orange button white on hover --
   less alarming than the dark-on-dark above, and still the button losing
   the fill that makes it the primary action. */
.nav-badge{display:inline-flex;align-items:center;justify-content:center;background:var(--violet);color:#fff;font-size:.62rem;font-weight:700;min-width:16px;height:16px;padding:0 4px;border-radius:100px;margin-left:5px;vertical-align:middle;line-height:1}
.menu-btn{display:none;width:42px;height:42px;border-radius:50%;border:1px solid var(--ink);background:none;cursor:pointer;
  flex-direction:column;align-items:center;justify-content:center;gap:5px}
.menu-btn span{width:15px;height:1.5px;background:var(--ink);transition:.2s}

/* ── OVERLAY ── */ .overlay{position:fixed;inset:0;z-index:200;background:var(--ink);color:var(--color-text);display:flex;flex-direction:column;
  justify-content:center;padding:48px;overflow-y:auto;transform:translateY(-100%);transition:transform .55s cubic-bezier(.76,0,.24,1)}
.overlay.open{transform:none}
.overlay-x{position:absolute;top:22px;right:40px;width:42px;height:42px;border-radius:50%;border:1px solid var(--color-border);
  background:none;color:var(--color-text);font-size:1.1rem;cursor:pointer}
/* legacy full-screen overlay type — superseded by the .ov-* drawer below. Kept out of the cascade rather than deleted: it outranked .ov-sub on specificity and rendered every drawer link at ~4.6rem serif. */ /* TYPE migrated to css/app.css 4-components (Phase 2B) */ /* LAYOUT migrated to css/app.css 4-components (Phase 2B) */ /* BUTTONS migrated to css/app.css 4-components (Phase 2B) */ /* CARDS migrated to css/app.css 4-components (Phase 2B) */ /* ── ANIMATIONS ── */ /* .reveal/.fade-in-up now centralized in css/app.css (Phase 4 motion) */ .d1{transition-delay:.08s}.d2{transition-delay:.16s}.d3{transition-delay:.24s}.d4{transition-delay:.32s}.d5{transition-delay:.4s}

/* gradient mesh hero bg */ .mesh{position:absolute;inset:0;overflow:hidden;pointer-events:none}
.blob{position:absolute;opacity:.5}
.blob.b1{width:560px;height:560px;background:var(--blob-color-1,#6d5efc);top:-140px;left:-100px;animation:drift-b1 18s ease-in-out infinite alternate;will-change:transform,border-radius,filter}
.blob.b2{width:460px;height:460px;background:var(--blob-color-2,#22d3ee);bottom:-120px;right:-80px;animation:drift-b2 23s ease-in-out infinite alternate;opacity:.38;will-change:transform,border-radius,filter}
.blob.b3{width:400px;height:400px;background:var(--blob-color-3,#f472b6);top:35%;left:52%;animation:drift-b3 29s ease-in-out infinite alternate;opacity:.30;will-change:transform,border-radius,filter}
@keyframes drift-b1{0%  {transform:translate(0,0) scale(1);border-radius:60% 40% 55% 45%/50% 60% 40% 50%;filter:blur(70px)}25%{transform:translate(80px,-60px) scale(1.08);border-radius:40% 60% 45% 55%/60% 40% 60% 40%;filter:blur(85px)}50%{transform:translate(110px,40px) scale(1.12);border-radius:55% 45% 60% 40%/45% 55% 45% 55%;filter:blur(65px)}75%{transform:translate(30px,90px) scale(1.05);border-radius:45% 55% 40% 60%/55% 45% 55% 45%;filter:blur(80px)}100%{transform:translate(-60px,50px) scale(.96);border-radius:60% 40% 55% 45%/40% 60% 40% 60%;filter:blur(72px)}}
@keyframes drift-b2{0%  {transform:translate(0,0) scale(1);border-radius:50% 50% 60% 40%/60% 40% 50% 50%;filter:blur(70px)}30%{transform:translate(-90px,50px) scale(1.1);border-radius:40% 60% 50% 50%/50% 50% 40% 60%;filter:blur(60px)}60%{transform:translate(-120px,-40px) scale(1.06);border-radius:60% 40% 40% 60%/40% 60% 60% 40%;filter:blur(88px)}100%{transform:translate(40px,-80px) scale(.94);border-radius:50% 50% 55% 45%/45% 55% 50% 50%;filter:blur(68px)}}
@keyframes drift-b3{0%  {transform:translate(0,0) scale(1);border-radius:55% 45% 50% 50%/50% 50% 55% 45%;filter:blur(75px)}20%{transform:translate(-70px,-80px) scale(1.12);border-radius:45% 55% 60% 40%/60% 40% 45% 55%;filter:blur(60px)}55%{transform:translate(60px,-110px) scale(.92);border-radius:60% 40% 45% 55%/40% 60% 55% 45%;filter:blur(90px)}80%{transform:translate(100px,30px) scale(1.08);border-radius:40% 60% 55% 45%/55% 45% 40% 60%;filter:blur(65px)}100%{transform:translate(-40px,70px) scale(1.04);border-radius:50% 50% 40% 60%/60% 40% 50% 50%;filter:blur(78px)}}
.grid-lines{position:absolute;inset:0;pointer-events:none;
  background-image:linear-gradient(var(--color-hairline) 1px,transparent 1px),linear-gradient(90deg,var(--color-hairline) 1px,transparent 1px);
  background-size:64px 64px;mask-image:radial-gradient(ellipse 80% 60% at 50% 40%,#000,transparent)}

/* marquee */ .marq{overflow:hidden;white-space:nowrap;-webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.marq-t{display:inline-flex;align-items:center;animation:scroll 34s linear infinite}
.marq-t .it{display:inline-flex;align-items:center;gap:14px;padding:0 30px;font-size:.85rem;color:var(--color-text-dim)}
.marq-t .dot{width:5px;height:5px;border-radius:50%;background:var(--violet)}
@keyframes scroll{to{transform:translateX(-50%)}}

/* dashboard mockup */ .mock{border-radius:18px;background:linear-gradient(160deg,var(--color-mock-2,#16161f),var(--color-mock-1,#0c0c12));border:1px solid rgba(255,255,255,.1);
  box-shadow:0 40px 80px -20px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.04) inset;overflow:hidden}
.mock-bar{display:flex;align-items:center;gap:7px;padding:13px 16px;border-bottom:1px solid rgba(255,255,255,.07)}
.mock-bar i{width:11px;height:11px;border-radius:50%;display:block}
.mock-dot1{background:#ff5f57}.mock-dot2{background:#febc2e}.mock-dot3{background:#28c840}

/* footer */ footer{background:var(--ink);color:var(--color-text);padding:90px 0 40px;position:relative;overflow:hidden}
.foot-link{color:var(--color-text-subtle);font-size:.92rem;line-height:2.1;transition:color .2s;display:block;width:fit-content}
.foot-link:hover{color:var(--color-text)}


/* ── Web 3.0 Interactions ── */ /* word-split */ .word-wrap{display:inline-block;overflow:hidden;vertical-align:bottom;line-height:1.18}
.split-word{display:inline-block;transform:translateY(110%);opacity:0;
  transition:transform .7s cubic-bezier(.23,1,.32,1),opacity .35s ease}
.words-in .split-word{transform:translateY(0);opacity:1}
/* card tilt */ .card,.product-row{transform-style:preserve-3d}
/* magnetic — ensure transform doesn't fight existing hover-lift */ .btn-grad,.nav-cta{will-change:transform}
/* blob parallax removed — CSS drift animation handles movement */ /* ── Orbital ecosystem — master rAF drives all motion ── */ #hero-orbit{ perspective: 1200px; }

.orbit-icon{
  will-change: transform, opacity;
  transform-style: preserve-3d;
  cursor: pointer;
  /* No backface-visibility:hidden here. A coin turning past 90deg would
     otherwise disappear for half of every flip rather than showing its
     reverse -- which is what made coins blink out while the ambient spin
     was running. The face mirrors briefly through the back half of the
     turn, which is what a real coin does. */
  pointer-events: auto;        /* container stays none; icons stay hoverable */
}

/* ── Hero phrase — living gradient sweep ── */ #hero-phrase{
  background: var(--gradient-phrase);
  background-size: 260% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: phraseGrad 5s linear infinite;
}
@keyframes phraseGrad {
0%{ background-position: 0%   center; }
  100%{ background-position: 100% center; }
}

/* ── Reduced motion — freeze gradient, static orbit ── */
@media (prefers-reduced-motion: reduce) {
#hero-phrase{ animation: none; background-position: 0% center; }
  .orbit-icon{ transition: none !important; }
  .hero-strip-track{ animation: none !important; }
}

/* ── Orbit vs strip — JS adds body.orbit-off when the ring can't clear text ── */ #hero-strip{ display: none; }                 /* hidden until orbit is suppressed */ body.orbit-off #hero-orbit{ display: none; }
body.orbit-off #hero-strip{ display: block; }

/* No-JS / small-screen safety net: never show the orbit on phones */
@media (max-width: 640px) {
#hero-orbit{ display: none; }
  #hero-strip{ display: block; }
}

/* ── Mobile product strip ── */ .hero-strip{
  margin-top: var(--space-xl);
  width: 100%;
  max-width: 100vw;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.hero-strip-track{
  display: flex;
  gap: 10px;
  width: max-content;
  animation: heroStripPan 26s linear infinite;
}
@keyframes heroStripPan {
0%{ transform: translateX(0); }
  100%{ transform: translateX(-50%); }
}
.hero-chip{
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 15px 9px 9px;
  border-radius: 999px;
  background: var(--color-glass-fill);
  border: 1px solid var(--color-glass-border);
  text-decoration: none;
  white-space: nowrap;
  flex-shrink: 0;
}
.hero-chip-ico{
  width: 30px; height: 30px;
  border-radius: 9px;
  background: var(--chip-grad, var(--gradient-brand));
  display: grid; place-items: center;
  font-size: 16px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18);
}
.hero-chip-name{
  font-size: .85rem;
  font-weight: 500;
  color: var(--color-text);
}

/* mark.kw and its reveal went with the highlighter they belonged to. The
   one emphasised phrase on the home page uses .focus-phrase now, like
   every other on the site. */

@media(max-width:900px){
.nav{padding:18px 22px}.nav.scrolled{padding:12px 22px}
  .nav-links,.nav-cta{display:none}.menu-btn{display:flex}
  .wrap{padding:0 22px}.sec{padding:80px 0}.sec-sm{padding:60px 0}
  footer .foot-grid{grid-template-columns:1.3fr 1fr 1fr!important;gap:32px!important}
}
@media(max-width:768px){
.pdp-features-grid{grid-template-columns:repeat(2,1fr)!important;gap:12px!important}
  .pdp-related-grid{grid-template-columns:repeat(2,1fr)!important;gap:12px!important}
  .pdp-hero-grid{grid-template-columns:1fr!important;gap:32px!important}
  .pdp-mock{display:none!important}
  .pdp-hero-text{text-align:center!important;display:flex!important;flex-direction:column!important;align-items:center!important}
  .pdp-hero-text .eyebrow{justify-content:center!important}
  .pdp-hero-btns{flex-direction:column!important;align-items:center!important;gap:10px!important}
  .pdp-hero-btns .btn{width:100%!important;justify-content:center!important;max-width:320px!important}
  .pdp-feat-card{align-items:center!important;text-align:center!important;padding:18px!important}
  .pdp-feat-icon{margin:0 auto 12px!important}
  .pdp-feat-label{font-size:.9rem!important}
  .pdp-cap-head{text-align:center!important}
  .pdp-cap-head .eyebrow{justify-content:center!important}
  .pdp-svc-icon{text-align:center!important}
}

@media(max-width:640px){
footer .foot-grid{grid-template-columns:1fr!important;gap:36px!important}
  #theme-toggle{width:36px!important;height:36px!important;bottom:14px!important;right:14px!important;font-size:.95rem!important}
  .mission-strip-grid{grid-template-columns:1fr 1fr!important}
  .mission-strip-grid>div{border-right:none!important;border-bottom:1px solid var(--line-dk);min-width:0;
    flex-direction:column!important;align-items:center!important;text-align:center!important;
    gap:10px!important;padding:24px 16px!important}
  .mission-strip-grid>div:nth-child(3),.mission-strip-grid>div:nth-child(4){border-bottom:none}
  .svc-grid{grid-template-columns:1fr!important}
  .reason-card{flex-direction:column!important}
  .svc-grid .card{align-items:center!important;text-align:center!important}
  .svc-section{padding-left:20px!important;padding-right:20px!important;padding-top:56px!important;padding-bottom:56px!important}
  .svc-hero{padding-top:110px!important;padding-bottom:48px!important}
  .cat-header{margin-bottom:28px!important;flex-direction:column!important;align-items:center!important;text-align:center!important}
  .cat-header>div{text-align:center!important}
  .cat-count{margin-top:4px}
  .pad-section{padding-top:40px!important;padding-bottom:40px!important}
  .sec-head{margin-bottom:32px!important}
  .hero-section{justify-content:flex-start!important;padding-top:100px!important;padding-bottom:32px!important;min-height:auto!important}
  #hero-content{padding-left:50px!important;padding-right:50px!important}
  .hero-h1{font-size:1.95rem!important}
  .svc-hero-h1{font-size:1.6rem!important;line-height:1.25!important}
  /* ── Contact Page ── */ .contact-hero{padding:120px 20px 60px!important}
  .contact-layout{grid-template-columns:1fr!important;gap:24px!important;max-width:100%!important;overflow:hidden!important}
  .contact-layout .card{padding:28px!important}
  .contact-layout .reveal>div:last-child .card{padding:20px!important}
  .form-row-2col{grid-template-columns:1fr!important;gap:12px!important}
  .contact-form-layout{grid-template-columns:1fr!important;gap:24px!important;max-width:100%!important;overflow:hidden!important}
  .contact-form-layout .card{padding:28px!important;min-width:0!important}
  .contact-form-fields{grid-template-columns:1fr!important;gap:12px!important}
  .contact-form-sidebar .card{padding:20px!important}
  .contact-faq{padding:60px 20px!important}
  /* ── Feature Grid Block ── */ .feature-card{flex-direction:column!important;align-items:center!important;text-align:center!important}
  .feature-card>div:first-child{margin:0 auto!important}
  /* ── Blog Page ── */ .blog-grid{grid-template-columns:1fr!important;gap:16px!important}
  /* ── About Page ── */ .about-story-grid{grid-template-columns:1fr!important;gap:40px!important}
  .about-values-grid{grid-template-columns:1fr!important;gap:14px!important}
  .about-values-grid .card{flex-direction:column!important;align-items:center!important;text-align:center!important}
  .about-values-grid .card-icon{text-align:center!important;width:100%!important;margin:0 auto 12px!important}
  .about-values-grid .card-title{text-align:center!important}
  .about-values-grid .card .body{text-align:center!important}
  .about-team-grid{grid-template-columns:repeat(2,1fr)!important;gap:20px!important;justify-items:center!important}
  .about-team-grid>div{text-align:center!important;display:flex!important;flex-direction:column!important;align-items:center!important}
  .about-team-grid .media-avatar{margin-left:auto!important;margin-right:auto!important}
  .hero-buttons{flex-direction:column!important;margin-bottom:32px!important;gap:12px!important;align-items:center!important}
  .hero-buttons .btn{max-width:280px!important;width:100%!important;justify-content:center!important}
  .hero-scroll{margin-top:8px;font-size:1.1rem!important}
  .scroll-text{display:none!important}
  .skill-card{text-align:center!important}
  .product-row-flex{flex-direction:column!important;align-items:center!important}
  .product-row-text{text-align:center!important;font-size:.9rem!important}
  .product-row-text .product-name{margin-bottom:4px!important;font-size:1.1rem!important}
  .product-row-text p{margin:8px 0!important;font-size:.85rem!important}
  .product-row-text>span{justify-content:center!important;display:flex!important;font-size:.78rem!important}
  .products-row-flex{flex-direction:column!important;align-items:center!important}
  .products-row-text{text-align:center!important}
  .products-row-text>div{justify-content:center!important;display:flex!important;flex-wrap:wrap!important}
  .products-row-text .product-learn-more{display:flex!important;align-items:center!important;justify-content:center!important;gap:16px!important}
  .product-row .desktop-arrow{display:none!important}
  .products-row-cta{display:none!important}
  .product-learn-more .cmp-btn{display:inline-flex!important;font-size:.75rem!important;padding:5px 12px!important;color:var(--color-text-subtle)!important;border-color:var(--color-border)!important}
  .foot-grid>div{text-align:center!important}
  .foot-brand-col{text-align:center!important}
    /* logo only - this used to match every anchor in the column, which put
     a white pill around the phone and email links */
  .foot-brand-col .foot-logo{display:block!important;margin:0 auto 20px!important;background:#fff!important;padding:12px!important;border-radius:12px!important;width:fit-content!important}
  .foot-brand-col img{height:110px!important;width:auto!important;margin:0 auto!important;display:block!important}
  .foot-social{justify-content:center!important}
  footer{text-align:center!important}
  footer a{display:block!important;margin:6px auto!important}
  /* keep the icon beside the text; block would stack them */
  footer .foot-contact .foot-link{display:inline-flex!important;align-items:center;justify-content:center;gap:7px;margin:8px auto!important}
  footer .foot-social a{display:inline-flex!important;margin:0!important}
  footer .foot-contact{text-align:center!important}
  /* column flex: children centre via align-items, not margin auto */
  footer .foot-contact{align-items:center!important}
  footer .foot-brand-col{align-items:center!important}
  footer .foot-col{align-items:center!important}
  footer .foot-social{align-items:center!important}
  footer .foot-contact a{text-align:center!important}
  .nav-right{display:flex!important}
  .menu-btn{display:flex!important;visibility:visible!important;opacity:1!important;position:relative!important;z-index:1001!important;width:auto!important;height:auto!important}
  .nav.scrolled{background:var(--ink)!important;color:var(--color-text)!important}
  .nav.scrolled .menu-btn{display:flex!important;visibility:visible!important;opacity:1!important;border-color:var(--color-border)!important}
  .nav.scrolled .menu-btn span{background:var(--color-text)!important}
  .nav.scrolled .nav-right{display:flex!important}
  /* ── Product Detail Page ── */ .pdp-hero-sec{padding:120px 20px 60px!important}
  .pdp-hero-grid{grid-template-columns:1fr!important;gap:36px!important}
  .pdp-mock{display:none!important}
  .pdp-hero-text{text-align:center!important;display:flex!important;flex-direction:column!important;align-items:center!important}
  .pdp-hero-text .eyebrow{justify-content:center!important}
  .pdp-hero-btns{flex-direction:column!important;gap:10px!important;align-items:center!important}
  .pdp-hero-btns .btn{width:100%!important;justify-content:center!important;max-width:320px!important}
  .pdp-features-grid{grid-template-columns:repeat(2,1fr)!important;gap:12px!important}
  .pdp-feat-card{padding:18px!important;align-items:center!important;text-align:center!important}
  .pdp-feat-icon{margin:0 auto 12px!important}
  .pdp-feat-label{font-size:.9rem!important}
  .pdp-related-grid{grid-template-columns:repeat(2,1fr)!important;gap:12px!important}
  .pdp-related-grid .card{padding:18px!important}
  .pdp-cap-head{text-align:center!important}
  .pdp-cap-head .eyebrow{justify-content:center!important}
  .pdp-svc-icon{text-align:center!important}
  /* ── Solutions Page ── */ .sol-card{grid-template-columns:1fr!important;gap:24px!important}
  .sol-card-icon{margin:0 auto!important}
  .sol-card-products{justify-content:center!important}
  .sol-card-products a{flex:1!important;min-width:140px!important;text-align:center!important}
  .sol-industry-card{grid-template-columns:1fr!important;gap:20px!important;padding:24px!important}
  .sol-industry-icon{margin:0 auto!important}
  .sol-industry-body{align-items:center!important;text-align:center!important}
  .sol-industry-body h2{text-align:center!important}
  .sol-industry-body p{text-align:center!important}
  .sol-industry-body>div{text-align:center!important}
  .sol-challenge-grid{grid-template-columns:1fr!important;gap:12px!important}
  .sol-products-row{justify-content:center!important}
  .sol-products-row a{flex:1!important;min-width:140px!important}
  .sol-services-row{justify-content:center!important}
}
/* ── Submit Button Animation ── */
@keyframes btn-spin {
80%{
    border: 2px solid transparent;
    border-left: 2px solid var(--violet);
  }
  100%{
    transform: rotate(1080deg);
    border: 2px solid var(--violet);
  }
}
@keyframes btn-check {
to{
    stroke: #17b978;
  }
}
.btn-submitting{
  pointer-events: none;
  opacity: 0.7;
}
.btn-submitting span{
  opacity: 0;
  transition: opacity 100ms;
}
.btn-submitting svg{
  animation: btn-spin 2s 500ms forwards;
  stroke: var(--violet);
}
.btn-submitted svg{
  animation: btn-check 500ms forwards;
  stroke: #17b978;
}
/* end PUBLIC CHROME (migrated) */

/* ── §19 NAVIGATION — design-system-v3 chrome ─────────────────────────────
   Overrides the migrated legacy nav rules (which sit earlier in this file).
   Design spec: solid black bar, 64px, 1px white-10% bottom border, depth-3,
   gray-400 links at 13px/500, orange on hover with an underline that grows
   from 0 to 100%.

   The legacy nav was transparent-until-scrolled; design-system-v3 specifies a permanently solid bar,so the .scrolled variant is reduced to a shadow change only. ───────────────────────────────────────────────────────────────────────── */ .nav{
  position:fixed;top:0;left:0;right:0;z-index:1000;
  display:flex;align-items:center;justify-content:space-between;
  height:64px;padding:0 var(--space-4);
  background:var(--color-ink);
  border-bottom:1px solid rgba(255,255,255,.1);
  box-shadow:var(--depth-3);
  transition:box-shadow .25s ease;
}
.nav.scrolled{padding:0 var(--space-4);box-shadow:var(--depth-4)}
.nav.on-dark:not(.scrolled) .nav-links a{color:var(--c-gray-400)}

/* brand */ .brand{display:flex;align-items:center;gap:10px;height:44px}
.brand-mark-wrap{
  width:40px;height:40px;border-radius:10px;background:#fff;
  display:grid;place-items:center;overflow:hidden;flex-shrink:0;
}
.brand-mark-wrap img{width:34px;height:34px;object-fit:contain;display:block}

/* links */ .nav-links{display:flex;align-items:center;gap:var(--space-3);list-style:none;margin:0;padding:0}
.nav-item{position:relative}
.nav-links a,.nav-trigger{
  display:inline-flex;align-items:center;gap:5px;
  color:var(--c-gray-400);font-size:13px;font-weight:500;
  text-decoration:none;background:none;border:none;padding:0;cursor:pointer;
  font-family:inherit;line-height:1;position:relative;opacity:1;
  transition:color .2s ease;
}
.nav-links a::after,.nav-trigger::after{
  content:'';position:absolute;bottom:-6px;left:0;width:0;height:2px;
  background:var(--color-brand);transition:width .3s ease;
}
.nav-links a:hover,.nav-trigger:hover,.nav-links a:focus-visible,.nav-trigger:focus-visible,.nav-trigger[aria-expanded="true"]{color:var(--color-brand)}
.nav-links a:hover::after,.nav-trigger:hover::after,.nav-trigger[aria-expanded="true"]::after{width:100%}
.nav-caret{transition:transform .25s ease}
.nav-trigger[aria-expanded="true"] .nav-caret{transform:rotate(180deg)}

/* dropdown panel — dark glass,matching the design's dark-section surfaces */ .nav-panel{
  position:absolute;top:calc(100% + 14px);left:50%;transform:translateX(-50%) translateY(-6px);
  min-width:520px;padding:var(--space-3);
  background:rgba(10,10,13,.96);
  -webkit-backdrop-filter:blur(24px);backdrop-filter:blur(24px);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--radius-lg);
  box-shadow:var(--depth-glass);
  opacity:0;visibility:hidden;transition:opacity .2s ease,transform .2s ease,visibility .2s;
  z-index:1001;
}
.nav-panel[hidden]{display:block}          /* animate instead of hard-hiding */ .nav-item.open .nav-panel{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0)}
.nav-panel-inner{display:flex;flex-wrap:wrap;gap:var(--space-4)}
.nav-col{min-width:190px;display:flex;flex-direction:column;gap:2px}
.nav-col-title{
  font-size:10px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;
  color:var(--color-brand);margin-bottom:8px;
}
.nav-link-item{
  display:flex!important;align-items:center;gap:9px;
  padding:7px 9px;border-radius:var(--radius-sm);
  color:var(--c-gray-300)!important;font-size:13px;font-weight:450;
  text-decoration:none;transition:background .15s ease,color .15s ease;
}
.nav-link-item::after{display:none}
.nav-link-item:hover{background:rgba(255,255,255,.07);color:#fff!important}
.nav-link-item .icon{color:var(--color-brand);opacity:.85}
.nav-panel-foot{
  flex-basis:100%;display:flex;gap:var(--space-3);flex-wrap:wrap;
  border-top:1px solid rgba(255,255,255,.1);margin-top:6px;padding-top:12px;
}
.nav-panel-cta{
  display:inline-flex!important;align-items:center;gap:6px;
  color:var(--color-brand)!important;font-size:12.5px;font-weight:600;text-decoration:none;
}
.nav-panel-cta::after{display:none}

/* CTA */ .nav-right{display:flex;align-items:center;gap:var(--space-2)}
.nav-cta{
  display:inline-flex;align-items:center;justify-content:center;
  padding:10px 20px;border-radius:var(--radius-md);
  background:var(--gradient-brand);color:#fff!important;
  font-size:13px;font-weight:600;text-decoration:none;border:none;
  box-shadow:0 8px 20px rgba(249,134,3,.32);
  transition:var(--transition-fast);
}
.nav-cta::after{display:none}
.nav-cta:hover{transform:translateY(-2px);box-shadow:0 14px 30px rgba(249,134,3,.45)}
.nav-cta:active{transform:translateY(0) scale(.97)}

.nav-badge{
  display:inline-flex;align-items:center;justify-content:center;
  background:var(--color-brand);color:#fff;font-size:.62rem;font-weight:700;
  min-width:16px;height:16px;padding:0 4px;border-radius:100px;margin-left:6px;line-height:1;
}

/* hamburger */ .menu-btn{
  display:none;width:42px;height:42px;border-radius:var(--radius-md);
  border:1px solid rgba(255,255,255,.18);background:none;cursor:pointer;
  flex-direction:column;align-items:center;justify-content:center;gap:5px;padding:0;
  transition:border-color .2s ease;
}
.menu-btn:hover{border-color:var(--color-brand)}
.menu-btn span{display:block;width:17px;height:2px;background:#fff;border-radius:2px;transition:transform .25s ease,opacity .25s ease}
.menu-btn[aria-expanded="true"] span:nth-child(1){transform:translateY(3.5px) rotate(45deg)}
.menu-btn[aria-expanded="true"] span:nth-child(2){transform:translateY(-3.5px) rotate(-45deg)}

/* ── mobile drawer ─────────────────────────────────────────────── Frame follows .pn-drawer-frame in design-system-v3: a 380px panel with a fixed 68px header and a scrolling body. The header must never scroll — the close button living inside the scroll box was the reason the drawer could trap you. ────────────────────────────────────────────────────────── */ .overlay{
  position:fixed;top:0;right:0;bottom:0;left:auto;z-index:1200;
  width:min(380px,100vw);
  background:var(--color-ink);color:#fff;
  display:flex;flex-direction:column;
  padding:0;
  border-left:1px solid rgba(255,255,255,.10);
  box-shadow:-24px 0 60px rgba(0,0,0,.45);
  transform:translateX(100%);transition:transform .28s cubic-bezier(.4,0,.2,1);
  overflow:hidden;
}
.overlay.open{transform:translateX(0)}

.ov-head{
  flex:0 0 auto;height:68px;padding:0 var(--space-3);
  display:flex;align-items:center;justify-content:space-between;
  border-bottom:1px solid rgba(255,255,255,.10);
  background:var(--color-ink);
}
/* The drawer logo gets the same backing as the header's. The mark carries
   solid black in its lower half and its wordmark, so on the drawer's dark
   panel only the orange half was surviving. Same reason the nav has one. */
.ov-brand{
  display:grid; place-items:center;
  width:40px; height:40px; border-radius:10px;
  background:#fff; overflow:hidden; flex-shrink:0;
  border:1px solid rgba(255,255,255,.55);
  box-shadow:0 2px 10px rgba(0,0,0,.22);
}
.ov-brand img{width:34px;height:34px;object-fit:contain;display:block}
.overlay-x{
  position:static;
  width:40px;height:40px;border-radius:var(--radius-md);
  border:1px solid rgba(255,255,255,.18);background:none;color:#fff;
  font-size:1.4rem;line-height:1;cursor:pointer;
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}
.overlay-x:hover{border-color:var(--color-brand);color:var(--color-brand)}

.ov-body{
  flex:1 1 auto;min-height:0;overflow-y:auto;
  -webkit-overflow-scrolling:touch;
  padding:var(--space-2) var(--space-3) var(--space-4);
}
.ov-nav{display:flex;flex-direction:column}

/* Triggers and top-level links share one row treatment. */ .ov-top,.ov-acc{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  width:100%;min-height:48px;padding:12px 6px;
  color:#fff;font-family:inherit;font-size:.95rem;font-weight:700;
  text-decoration:none;background:none;border:none;
  border-bottom:1px solid rgba(255,255,255,.08);
  cursor:pointer;text-align:left;
}
.ov-top:hover,.ov-acc:hover{color:var(--color-brand)}
.ov-caret{transition:transform .25s ease;flex-shrink:0;opacity:.55}
.ov-acc[aria-expanded="true"]{color:var(--color-brand);border-bottom-color:transparent}
.ov-acc[aria-expanded="true"] .ov-caret{transform:rotate(180deg);opacity:1}

/* Children sit in a tinted block so the accordion reads as nested. */ .ov-panel{
  background:rgba(255,255,255,.045);
  border-radius:var(--radius-md);
  padding:4px 8px 8px;
  margin:0 0 var(--space-2);
}
.ov-sub-title{
  font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;
  color:var(--color-brand);margin:12px 4px 2px;
}
.ov-sub{
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  min-height:44px;padding:10px 4px;
  color:var(--c-gray-300);font-size:.85rem;font-weight:500;text-decoration:none;
}
.ov-sub span{opacity:.35;font-size:.8rem}
.ov-sub:hover{color:#fff}
.ov-sub:hover span{opacity:.8}
.ov-sub-all{color:var(--color-brand);font-weight:700}

.ov-cta{
  display:block;width:100%;margin-top:var(--space-3);
  padding:14px var(--space-3);border-radius:var(--radius-md);
  background:linear-gradient(135deg,var(--color-brand),#ff6b35);
  color:#fff;font-weight:800;font-size:.95rem;text-align:center;text-decoration:none;
  box-shadow:0 8px 24px rgba(249,134,3,.28);
}
/* These were five 11px links inside one centred sentence, separated by
   middots. The text was small enough to be hard to read on a phone and
   the tap targets were the height of the text itself and touching each
   other, so Careers and Contact were a coin toss. Now each one is its own
   44px row in a wrapping band, which is also why the separators went:
   spacing does that job once the links have room. */
.ov-note{
  margin-top:var(--space-3);
  display:flex;flex-wrap:wrap;justify-content:center;gap:2px;
  font-size:13px;color:var(--c-gray-400);
}
.ov-note a{
  color:inherit;text-decoration:none;
  display:inline-flex;align-items:center;
  min-height:44px;padding:0 12px;border-radius:var(--radius-sm);
}
.ov-note a:hover{color:#fff;background:rgba(255,255,255,.06)}

/* ── nav responsive ── */
@media(max-width:1023px){
.nav-links{display:none}
  .menu-btn{display:flex}
  .nav{padding:0 var(--space-3)}
}
@media(max-width:600px){
.nav-cta{display:none}     /* CTA lives at the bottom of the overlay on phones */
}
@media(prefers-reduced-motion:reduce){
.overlay{transition:none}
  .nav-panel{transition:none}
  .nav-cta:hover{transform:none}
}
/* end NAVIGATION */ /* ── §20 FOOTER — interlinking mesh ─────────────────────────────────────── Five link columns so every content pillar is crawlable from every page. This is where breadth lives,which is what lets the primary nav stay at five items. Styled as a dark section per design-system-v3. ───────────────────────────────────────────────────────────────────────── */ footer{
  position:relative;overflow:hidden;
  background:var(--color-ink);color:#fff;
  padding:var(--space-7) 0 var(--space-4);
  border-top:1px solid rgba(255,255,255,.08);
}

.foot-grid{
  display:grid;
  grid-template-columns:1.7fr repeat(4,1fr);
  gap:var(--space-5) var(--space-4);
  padding-bottom:var(--space-6);
}

.foot-brand-col{display:flex;flex-direction:column;align-items:flex-start}
.foot-logo{
  display:inline-block;margin-bottom:var(--space-3);
  background:#fff;padding:8px;border-radius:var(--radius-md);
}
.foot-logo img{height:72px;width:auto;display:block}
.foot-blurb{
  color:var(--c-gray-400);font-size:.94rem;line-height:1.7;
  max-width:360px;font-weight:400;margin:0 0 var(--space-3);
}
.foot-contact{display:flex;flex-direction:column;gap:4px;margin-bottom:var(--space-3)}
.foot-contact .foot-link{font-size:.9rem}
.foot-contact .icon{color:var(--color-brand)}

.foot-social{display:flex;gap:10px}
.foot-social a{
  width:38px;height:38px;border-radius:var(--radius-md);
  border:1px solid rgba(255,255,255,.22);
  display:grid;place-items:center;
  /* --c-gray-400 against the footer's brown left these looking switched
     off next to the orange headings beside them. These are the values the
     dark sections use for dimmed text and hairlines. */
  color:rgba(255,255,255,.78);
  transition:var(--transition-fast);
}
.foot-social a:hover{
  background:var(--color-brand-tint);border-color:var(--color-brand);
  color:var(--color-brand);transform:translateY(-2px);
}

.foot-col{display:flex;flex-direction:column}
.foot-head{
  font-size:10px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--color-brand);margin-bottom:var(--space-2);
}
.foot-link{
  color:var(--c-gray-300);font-size:.9rem;line-height:2.05;   /* gray-400 measured ~5.9:1 here; 300 gives ~8.9 */
  text-decoration:none;display:block;width:fit-content;
  transition:color .18s ease,transform .18s ease;
}
.foot-link:hover{color:#fff;transform:translateX(3px)}
.foot-all{color:var(--color-brand);font-weight:600;margin-top:6px}
.foot-all:hover{color:var(--color-brand)}
.foot-link.with-icon{display:inline-flex;align-items:center;gap:7px}

/* newsletter */ .foot-sub{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-4);flex-wrap:wrap;
  padding:var(--space-4) 0;
  border-top:1px solid rgba(255,255,255,.08);
}
.foot-sub-copy{max-width:420px}
.foot-sub-form{min-width:300px;flex:0 1 420px}
.foot-sub-row{display:flex;gap:8px}
.foot-sub-input{
  flex:1;min-width:0;
  background:rgba(255,255,255,.06);
  border:1px solid rgba(255,255,255,.14);
  border-radius:var(--radius-md);
  padding:12px 14px;font-size:.9rem;color:#fff;
  outline:none;font-family:inherit;
  transition:border-color .18s ease,box-shadow .18s ease;
}
.foot-sub-input::placeholder{color:var(--c-gray-500,#6b7280)}
.foot-sub-input:focus{
  border-color:var(--color-brand);
  box-shadow:0 0 0 3px var(--color-brand-tint);
}
.foot-sub-ok{color:var(--color-brand);font-size:.9rem;margin:0;font-weight:600}
.foot-sub-err{color:#f87171;font-size:.8rem;margin-top:6px}

/* legal */ .foot-legal{
  border-top:1px solid rgba(255,255,255,.08);
  padding-top:var(--space-3);
  display:flex;justify-content:space-between;align-items:center;
  flex-wrap:wrap;gap:12px;
}
.foot-legal span,.foot-legal a{color:var(--c-gray-500,#6b7280);font-size:.85rem;text-decoration:none}
.foot-legal a:hover{color:var(--color-brand)}

.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0;
}

/* ── footer responsive ── */
@media(max-width:1023px){
.foot-grid{grid-template-columns:1fr 1fr 1fr;gap:var(--space-4)}
  .foot-brand-col{grid-column:1 / -1}
  .foot-blurb{max-width:520px}
}
@media(max-width:640px){
footer{padding:var(--space-6) 0 var(--space-3)}
  .foot-grid{grid-template-columns:1fr 1fr;gap:var(--space-4)}
  .foot-sub{flex-direction:column;align-items:stretch}
  .foot-sub-form{min-width:0;flex:1 1 auto}
  .foot-legal{justify-content:flex-start;gap:8px}
  .foot-logo img{height:60px}
}
@media(max-width:400px){
.foot-grid{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
.foot-link:hover,.foot-social a:hover{transform:none}
}
/* end FOOTER */

/* ── §21 PHASE-3 CORRECTIONS ──────────────────────────────────────────────
   Two issues found during footer verification.

   1. --c-gray-500 was omitted from the section-1 primitives, so every
      `var(--c-gray-500, ...)` fell through to the legacy #6c6a78 rather than
      the design's #6b7280. Defining it closes the gap in the gray ramp.

   2. Footer legal text measured 3.83:1 against the ink background - below the
      4.5:1 AA threshold for small text. Promoted to the gray-400 step, which
      measures ~7.97:1. This is unrelated to the accepted white-on-orange
      deviation in section 17; that one is a deliberate,recorded decision,whereas this was simply too dim. ───────────────────────────────────────────────────────────────────────── */ :root{
  --c-gray-500:#6b7280;
}

.foot-legal span,.foot-legal a{color:var(--c-gray-400)}
.foot-legal a:hover{color:var(--color-brand)}

/* keep the muted-meta roles on the same accessible step */ .post-meta,.image-card-meta,.team-role,.stat-label{color:var(--c-gray-400)}
.light-section .post-meta,.light-section .image-card-meta,.light-section .team-role,.light-section .stat-label{color:var(--c-gray-600)}
/* end PHASE-3 CORRECTIONS */

/* ── §22 HOMEPAGE SECTIONS ────────────────────────────────────────────────
   Layout for the restructured homepage narrative. Visual treatment (surface,
   depth, colour) comes from the design-system sections above; this only handles composition and the few section-specific pieces. ───────────────────────────────────────────────────────────────────────── */ .section-head{margin-bottom:var(--space-6);text-align:center;max-width:760px;margin-left:auto;margin-right:auto}
.section-head .eyebrow{display:inline-block}
.section-foot{margin-top:var(--space-5);display:flex;justify-content:center;gap:var(--space-2);flex-wrap:wrap}

/* trust / stats */ .stat-row{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-3)}
.stat-cell{text-align:center;padding:var(--space-4) var(--space-2)}

/* problem -> outcome */ .problem-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
.problem-card{display:flex;flex-direction:column;gap:6px}
.problem-text,.outcome-text{font-size:.9rem;line-height:1.6;margin:0}
.problem-text{color:var(--c-gray-600)}
.outcome-text{color:var(--c-gray-800)}
.problem-tag,.outcome-tag{
  display:inline-block;font-size:9.5px;font-weight:700;letter-spacing:1.2px;
  text-transform:uppercase;padding:2px 7px;border-radius:6px;margin-right:6px;
  vertical-align:1px;
}
.problem-tag{background:var(--c-gray-200);color:var(--c-gray-600)}
.outcome-tag{background:var(--color-brand-tint);color:var(--color-brand)}

/* how we help - one reusable card+links unit, no anchor-in-anchor */
.help-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--space-4) var(--space-5)}
.help-unit{display:grid;grid-template-columns:1fr 1.35fr;gap:var(--space-4);align-items:stretch}
.help-card{
  display:flex;flex-direction:column;align-items:flex-start;text-align:left;
  padding:var(--space-3);min-height:168px;text-decoration:none;
}
.help-card .card-icon{margin-bottom:var(--space-2)}
.help-card .card-title{margin-bottom:6px}
.help-card .body{margin:0;flex:1}
.help-links{
  display:flex;flex-direction:column;justify-content:center;gap:10px;
  padding:var(--space-2) 0;
}
.help-link{
  display:inline-flex;align-items:center;gap:6px;
  color:var(--c-gray-300);font-size:.88rem;line-height:1.4;   /* dark-tone default; light restored below */
  text-decoration:none;transition:color .18s ease,transform .18s ease;
}
.help-link:hover{color:var(--color-brand);transform:translateX(2px)}
.help-link .icon{color:var(--color-brand);opacity:.7;flex-shrink:0}

/* products */ .product-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
.card-more{
  display:inline-flex;align-items:center;gap:6px;margin-top:var(--space-2);
  color:var(--color-brand);font-size:.85rem;font-weight:600;
}
.badge-beta{margin-left:8px;vertical-align:2px}

/* industries */ .industry-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--space-3);max-width:1000px;margin:0 auto}
.industry-name{font-size:.92rem;font-weight:600;margin-top:10px;display:block}

/* testimonials */ .quote-text{font-size:1rem;line-height:1.75;color:var(--c-gray-300);margin:0 0 var(--space-3)}
.quote-by{display:flex;align-items:center;gap:12px}

/* cta */ .cta-lead{margin:var(--space-2) auto var(--space-4);max-width:520px}
.cta-actions{display:flex;gap:var(--space-2);justify-content:center;flex-wrap:wrap}

/* ── homepage responsive ── */
@media(max-width:1023px){
.stat-row{grid-template-columns:repeat(2,1fr)}
  .problem-grid,.product-grid{grid-template-columns:repeat(2,1fr)}
  .help-grid{grid-template-columns:1fr}
  .help-unit{grid-template-columns:1fr 1.15fr}
}
@media(max-width:767px){
.section-head{margin-bottom:var(--space-4)}
  .problem-grid,.product-grid{grid-template-columns:1fr}
  .help-grid{grid-template-columns:1fr;gap:var(--space-4)}
  .help-unit{grid-template-columns:1fr;gap:var(--space-2)}
  .help-card{min-height:0}
  .help-links{padding-top:0;border-top:1px solid var(--color-border);padding-top:var(--space-2)}
  .industry-grid{grid-template-columns:repeat(2,1fr)}
  .cta-actions{flex-direction:column;align-items:stretch}
  .cta-actions .btn{width:100%}
}
@media(max-width:480px){
.stat-row{grid-template-columns:1fr 1fr;gap:var(--space-2)}
  .stat-cell{padding:var(--space-3) var(--space-1)}
}
@media(prefers-reduced-motion:reduce){
.help-link:hover{transform:none}
}
/* end HOMEPAGE SECTIONS */

/* ── §23 ACCESSIBLE ORANGE FOR TEXT ON LIGHT ─────────────────────────────
   Brand orange #F98603 as TEXT on a light background measures 2.49:1 - the
   mirror of the white-on-orange issue recorded in section 17.

   Scope note, because these are not the same decision:

     * design-specified uses stay as designed. design-system-v3 sets
       .serif-accent and .eyebrow to var(--primary) on light sections, and the
       standing decision (2026-08-22) is to implement the design exactly and
       accept the AA shortfall. Those are untouched here.

     * components introduced during implementation - .outcome-tag and
       .img-fallback - have no design mandate, so there is no fidelity reason
       to ship them inaccessible. They move to a darkened orange that keeps
       the brand read while clearing AA.

   --color-brand-text is deliberately a separate token from --color-brand, so
   the accent colour itself is unchanged; only text-on-light usages differ. ───────────────────────────────────────────────────────────────────────── */ :root{
  --color-brand-text:#9A5200;
}

/* my own additions - made accessible */ .outcome-tag{background:var(--color-brand-tint);color:var(--color-brand-text)}
.img-fallback{color:var(--color-brand-text)}
.light-section .card-more{color:var(--color-brand-text)}
.light-section .help-link{color:var(--c-gray-600)}
.light-section .help-link:hover{color:var(--color-brand-text)}
.light-section .foot-all{color:var(--color-brand-text)}

/* dark sections keep the bright accent - it reads correctly on ink */ .dark-section .card-more,.dark-section .img-fallback{color:var(--color-brand)}

/* rhythm: Insights follows Industries, so alternate its surface */

/* end ACCESSIBLE ORANGE */

/* ── §24 DETAIL PAGE TEMPLATES (service / product) ───────────────────────
   Shared layout for the outcome-led detail sequence. Visual treatment comes
   from the design-system sections above; this handles composition only. ───────────────────────────────────────────────────────────────────────── */ /* breadcrumbs — public site previously had none anywhere */ .crumbs{
  display:flex;align-items:center;gap:7px;flex-wrap:wrap;
  font-size:.8rem;color:var(--c-gray-400);margin-bottom:var(--space-4);
}
.crumbs a{color:var(--c-gray-400);text-decoration:none;transition:color .18s ease}
.crumbs a:hover{color:var(--color-brand)}
.crumbs [aria-current]{color:#fff;font-weight:500}
.crumbs .icon{opacity:.5}
.light-section .crumbs a .crumbs a{color:var(--c-gray-600)}
.light-section .crumbs [aria-current] .crumbs [aria-current]{color:var(--c-gray-900)}

/* hero */ .svc-hero-grid{
  display:grid;grid-template-columns:1fr auto;gap:var(--space-5);
  align-items:center;
}
.svc-hero-lead{margin:var(--space-2) 0 var(--space-4);max-width:560px}
.svc-hero-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}
.svc-hero-mark{
  width:132px;height:132px;border-radius:var(--radius-2xl);
  display:grid;place-items:center;flex-shrink:0;
  background:var(--color-brand-tint);
  border:1px solid var(--color-brand-line);
  color:var(--color-brand);
  box-shadow:0 0 60px rgba(249,134,3,.18);
}

/* problem -> outcome stack */ .po-stack{display:flex;flex-direction:column;gap:var(--space-3)}
.po-block{
  padding:var(--space-4);border-radius:var(--radius-lg);
  background:var(--c-gray-50);border-left:3px solid var(--c-gray-300);
}
.po-block-out{
  background:var(--color-brand-tint);
  border-left-color:var(--color-brand);
}
.po-text{
  font-size:1.12rem;line-height:1.65;color:var(--c-gray-700);
  margin:10px 0 0;font-weight:400;
}
.po-text-out{color:var(--c-gray-900);font-weight:500}

/* what's included */ .include-grid{
  display:grid;grid-template-columns:repeat(2,1fr);
  gap:var(--space-2) var(--space-4);max-width:900px;margin:0 auto;
}
.include-item{
  display:flex;align-items:flex-start;gap:11px;
  padding:13px 0;border-bottom:1px solid var(--c-gray-200);
  font-size:.98rem;color:var(--c-gray-700);line-height:1.55;
}
.include-item .icon{color:var(--color-brand);flex-shrink:0;margin-top:2px}

/* faq */ .faq-list{display:flex;flex-direction:column;gap:10px;max-width:760px;margin:0 auto}
.faq-item{
  border:1px solid var(--c-gray-200);border-radius:var(--radius-lg);
  background:#fff;overflow:hidden;transition:box-shadow .2s ease;
}
.faq-item[open]{box-shadow:var(--depth-2)}
.faq-item summary{
  display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);
  padding:17px var(--space-3);cursor:pointer;list-style:none;
  font-size:1rem;font-weight:600;color:var(--c-gray-900);
}
.faq-item summary::-webkit-details-marker{display:none}
.faq-item summary:hover{color:var(--color-brand-text,#9A5200)}
.faq-item summary .icon{transition:transform .22s ease;flex-shrink:0;color:var(--color-brand)}
.faq-item[open] summary .icon{transform:rotate(180deg)}
.faq-answer{
  padding:0 var(--space-3) var(--space-3);
  font-size:.96rem;line-height:1.72;color:var(--c-gray-600);
}

/* quotes on light surfaces */ .quote-light{color:var(--c-gray-700)}

/* ── detail responsive ── */
@media(max-width:900px){
.svc-hero-grid{grid-template-columns:1fr;gap:var(--space-4)}
  .svc-hero-mark{width:96px;height:96px;order:-1}
}
@media(max-width:767px){
.include-grid{grid-template-columns:1fr}
  .svc-hero-actions{flex-direction:column;align-items:stretch}
  .svc-hero-actions .btn{width:100%}
  .po-text{font-size:1.02rem}
}
/* end DETAIL PAGE TEMPLATES */

/* ── §25 PRODUCT DETAIL ADDITIONS ────────────────────────────────────────
   Only the pieces unique to the product template; everything else reuses the shared detail patterns in section 24. ───────────────────────────────────────────────────────────────────────── */ /* who it's for */ .audience-strip{
  display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;
  padding:var(--space-2) 0;
}
.audience-label{
  font-size:10px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;
  color:var(--c-gray-500);flex-shrink:0;
}
.audience-chips{display:flex;gap:8px;flex-wrap:wrap}
.audience-chips .chip{text-decoration:none}

/* see it */ .see-it{
  display:flex;align-items:center;justify-content:space-between;
  gap:var(--space-4);flex-wrap:wrap;
  padding:var(--space-4);border-radius:var(--radius-xl);
  background:rgba(255,255,255,.04);
  border:1px solid rgba(255,255,255,.12);
}
.see-it-actions{display:flex;gap:var(--space-2);flex-wrap:wrap}

/* status badges sit beside the H1 */ h1 .badge-beta,h1 .badge-coming-soon{
  vertical-align:middle;margin-left:12px;
  font-size:10px;position:relative;top:-4px;
}

@media(max-width:767px){
.see-it{flex-direction:column;align-items:stretch}
  .see-it-actions{flex-direction:column}
  .see-it-actions .btn{width:100%}
  .audience-strip{flex-direction:column;align-items:flex-start;gap:10px}
}
/* end PRODUCT DETAIL ADDITIONS */ /* ── §26 SERVICES INDEX ────────────────────────────────────────────────── Replaces the per-category inline accent gradients that were the last carrier of the legacy violet/cyan brand on the public site. ───────────────────────────────────────────────────────────────────────── */ .pillar-row{
  display:grid;grid-template-columns:repeat(4,1fr);gap:var(--space-3);
}
.pillar{
  display:flex;align-items:center;gap:11px;
  font-size:.92rem;font-weight:500;color:var(--c-gray-700);line-height:1.4;
}
.pillar .icon{color:var(--color-brand);flex-shrink:0}

.cat-header{
  display:flex;align-items:flex-end;justify-content:space-between;
  gap:var(--space-3);flex-wrap:wrap;margin-bottom:var(--space-5);
}
.cat-count{font-size:.82rem;color:var(--c-gray-500)}

.svc-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
.svc-card{display:flex;flex-direction:column}
.svc-card-tagline{
  font-size:.8rem;font-weight:600;letter-spacing:.02em;text-transform:uppercase;
  color:var(--color-brand-text,#9A5200);margin:0 0 10px;line-height:1.4;
}
.svc-card .body{flex:1}

.reason-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
.reason-card{display:flex;flex-direction:column}

@media(max-width:1023px){
.pillar-row{grid-template-columns:repeat(2,1fr);gap:var(--space-3) var(--space-4)}
  .svc-grid,.reason-grid{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:767px){
.pillar-row{grid-template-columns:1fr}
  .svc-grid,.reason-grid{grid-template-columns:1fr}
  .cat-header{flex-direction:column;align-items:flex-start;gap:8px}
}
/* end SERVICES INDEX */ .ind-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
.ind-card{display:flex;flex-direction:column}
.ind-card .body{flex:1}
.ind-meta{display:flex;gap:14px;font-size:.78rem;color:var(--c-gray-500);margin:10px 0 4px}
@media(max-width:1023px){.ind-grid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:767px){.ind-grid{grid-template-columns:1fr}}

/* ── §27 ZONE GRADIENTS,TEXTURE LAYERS,GRADIENT BORDERS,WHITE GLASS ──── Implements the atmospheric layer added in the later design iteration (deepseek_html_20260822). None of this existed before: dark sections were a flat #060608 fill. The stack,bottom to top: 1. zone gradient 6 shades white -> black on a directional axis 2. texture layer grayscale image,low opacity,mix-blend-mode:overlay 3. tint overlay rgba(6,6,8,.4) so text stays legible over both 4. content z-index 2 On the texture: the design file used picsum.photos placeholders,which must not ship. These are generated abstract grayscale fields - deliberately GRAYSCALE,because under overlay blending a neutral source adds light/dark depth without shifting the hue of the zone gradient beneath it. Nothing here depicts a client,a person or a project,so it introduces no fabricated business content. ───────────────────────────────────────────────────────────────────────── */ :root{
  /* 6-shade ramp, white -> black */
  --shade-1:#ffffff; --shade-2:#d4d4d4; --shade-3:#a8a8a8;
  --shade-4:#7a7a7a; --shade-5:#3a3a3f; --shade-6:#060608;

  /* white glassmorphism ladder */
  --white-50:rgba(255,255,255,.5);  --white-60:rgba(255,255,255,.6);
  --white-70:rgba(255,255,255,.7);  --white-80:rgba(255,255,255,.8);
  --white-90:rgba(255,255,255,.9);  --white-95:rgba(255,255,255,.95);

  /* gradient borders */
  --grad-border-light:linear-gradient(135deg,rgba(249,134,3,.6),rgba(255,255,255,.4),rgba(249,134,3,.2));
  --grad-border-strong:linear-gradient(135deg,var(--color-brand),#ff6b6b,var(--color-brand));
  --grad-border-white:linear-gradient(135deg,rgba(255,255,255,.8),rgba(249,134,3,.6),rgba(255,255,255,.3));
  --grad-border-subtle:linear-gradient(135deg,rgba(255,255,255,.4),rgba(249,134,3,.3),rgba(255,255,255,.2));
}

/* The zone-gradient system was removed in Phase A. It painted a white-to-black ramp behind "dark" sections and then dimmed it,which produced a second,washed-out dark surface the design guide does not define. The guide has exactly two section surfaces. */ /* ---- texture layer ---- */ .section-texture{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-size:cover;background-position:center;
  opacity:.3;mix-blend-mode:overlay;
}
.section-texture--hero{background-image:url('/images/texture/texture-hero.jpg');opacity:.25}
.section-texture--deep{background-image:url('/images/texture/texture-deep.jpg')}
.section-texture--soft{background-image:url('/images/texture/texture-soft.jpg')}

/* ---- gradient borders ---- Border-image via a masked pseudo-element: paints a gradient ring without an extra wrapper element and without faking it with a background inset. */ .grad-border{position:relative}
.grad-border::before{
  content:'';position:absolute;inset:0;border-radius:inherit;
  padding:2px;background:var(--grad-border-light);
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  mask-composite:exclude;pointer-events:none;z-index:1;
}
.grad-border-strong::before{background:var(--grad-border-strong)}
.grad-border-white::before{background:var(--grad-border-white)}
.grad-border-subtle::before{background:var(--grad-border-subtle)}
.grad-border > *{position:relative;z-index:2}

/* ---- white glassmorphism ladder (light surfaces) ---- */ .light-section .glass-medium{background:var(--white-70);border-color:var(--white-90)}
.light-section .glass-heavy{background:var(--white-80);border-color:var(--white-95)}
.light-section .glass-extra{background:var(--white-90);border-color:var(--white-95)}
/* on a zoned dark section the ramp runs light -> dark,so white glass reads correctly at the light end where dark glass would disappear */ .dark-section .glass-medium{background:var(--white-70);border:1px solid var(--white-90);color:var(--c-gray-900)}
.dark-section .glass-heavy{background:var(--white-80);border:1px solid var(--white-95);color:var(--c-gray-900)}
.dark-section .glass-extra{background:var(--white-90);border:1px solid var(--white-95);color:var(--c-gray-900)}
.dark-section .glass-medium .stat-label,.dark-section .glass-heavy .stat-label,.dark-section .glass-extra .stat-label{color:var(--c-gray-600)}
.dark-section .glass-medium .stat-num,.dark-section .glass-heavy .stat-num,.dark-section .glass-extra .stat-num{text-shadow:none}

/* ---- light CTA variant ---- */ .cta-banner-light{
  position:relative;overflow:hidden;max-width:1240px;margin:0 auto;text-align:center;
  border-radius:var(--radius-2xl);padding:var(--space-8) var(--space-5);
  color:var(--c-gray-900);box-shadow:var(--depth-4);
  background:linear-gradient(180deg,rgba(255,255,255,.85) 0%,rgba(255,255,255,.95) 100%),
             url('/images/texture/texture-soft.jpg') center/cover;
}
.cta-banner-light .cta-glass{
  background:var(--white-80);border:1px solid var(--white-95);
  color:var(--c-gray-900);
}
.cta-banner-light .cta-lead{color:var(--c-gray-600)}
.cta-banner-light .cta-orb{opacity:.55}

/* ---- accessibility / performance ---- */
@media(prefers-reduced-motion:reduce){
.grad-border::before{transition:none}
}
@media(max-width:767px){
/* blend + blur + cover images are expensive on phones for a decorative layer */ .section-texture{opacity:.18}
}
/* end ZONE GRADIENTS + TEXTURE */

/* ── §28 ZONE LEGIBILITY CORRECTION ──────────────────────────────────────
   The zone ramp runs pure white -> near black. With the design file's
   rgba(6,6,8,.4) tint, the light end of the ramp composites to rgb(155,155,156)
   - a mid grey. Measured against it:

       white headings   2.76:1   (fails even the 3.0 large-text bar)
       gray-400 body    1.09:1   (effectively invisible)




/* end ZONE LEGIBILITY CORRECTION */ .media-img{width:100%;height:100%;object-fit:cover;display:block}
.post-thumb .media-img,.image-card-photo .media-img{transition:transform .5s ease}
.media-avatar .media-img{border-radius:50%}

/* ── §29 CASE STUDIES ──────────────────────────────────────────────────── Index empty state,metric row,and detail-page pieces. ───────────────────────────────────────────────────────────────────────── */ .cs-empty{text-align:center;max-width:640px;margin:0 auto}
.cs-empty .card-icon{margin:0 auto var(--space-2)}
.cs-empty .body{max-width:480px;margin:0 auto}
.cs-empty-actions{display:flex;gap:var(--space-2);justify-content:center;flex-wrap:wrap;margin-top:var(--space-4)}

.cs-filters{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;margin-bottom:var(--space-4)}

.cs-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--space-3);max-width:1000px;margin:0 auto}
.cs-metric{text-align:center;padding:var(--space-3) var(--space-2)}
.cs-metrics-mini{display:flex;gap:14px;flex-wrap:wrap;margin-top:10px;font-size:.78rem;color:var(--c-gray-600)}
.cs-metrics-mini strong{color:var(--color-brand-text,#9A5200);font-weight:700}

.cs-client{font-size:.92rem;color:var(--c-gray-200);margin-top:var(--space-2)}
.cs-client strong{color:#fff}

@media(max-width:767px){
.cs-empty-actions{flex-direction:column;align-items:stretch}
  .cs-empty-actions .btn{width:100%}
  .cs-filters{flex-direction:column;align-items:flex-start;gap:10px}
}
/* end CASE STUDIES */ /* crumb bar for CMS-driven pages */ .crumb-bar{background:var(--color-ink);padding:80px var(--space-4) 0}
.crumb-bar .wrap{padding-top:8px}
@media(max-width:767px){.crumb-bar{padding:72px var(--space-3) 0}}

/* ── ADMIN — Pagination,empty state,small buttons,sidebar icons ───────── Added with the admin design pass. The paginator had NO styling at all: Laravel's default view emits Tailwind markup and this project loads no Tailwind,so every ->links() call rendered as bare text. ───────────────────────────────────────────────────────────────────────── */ .admin-pagination{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:14px 20px}
.admin-pagination-info{font-size:.8rem;color:var(--admin-text-muted)}
.admin-pagination-list{display:flex;align-items:center;gap:4px;list-style:none;margin:0;padding:0}
.admin-pagination-link{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:34px;padding:0 10px;border-radius:9px;border:1px solid var(--admin-border);background:var(--admin-surface);color:var(--admin-text);font-size:.82rem;font-weight:500;text-decoration:none;transition:all .18s}
.admin-pagination-link:hover{border-color:var(--admin-accent);color:var(--admin-accent-text)}
.admin-pagination-link.is-active{background:var(--admin-accent);border-color:var(--admin-accent);color:var(--admin-text-on-accent);font-weight:600}
.admin-pagination-link.is-disabled{opacity:.4;pointer-events:none}
.admin-pagination-gap{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:34px;color:var(--admin-text-muted);font-size:.82rem}

/* Table footer — hosts pagination inside a padded,borderless card table */ .admin-table-foot{border-top:1px solid var(--admin-border)}

/* Shared empty state (replaces the colspan-row treatment) */ .admin-empty{display:flex;flex-direction:column;align-items:center;gap:18px;text-align:center;padding:56px 24px}
.admin-empty-msg{color:var(--admin-text-muted);font-size:.95rem;margin:0}

/* Small button — was hand-inlined as padding:6px 12px;font-size:.75rem on every row action,in every table. */ .admin-btn-sm{padding:6px 12px;font-size:.75rem;border-radius:8px}

/* Sidebar sprite icons (replaced emoji: they ignore currentColor,so they could not pick up the hover/active text colour) */ .admin-sidebar-nav a svg{flex-shrink:0;opacity:.7;transition:opacity .18s}
.admin-sidebar-nav a:hover svg,.admin-sidebar-nav a.active svg{opacity:1}
#admin-theme-toggle{display:inline-flex;align-items:center;justify-content:center}

@media(max-width:767px){
.admin-pagination{justify-content:center;padding:14px}
  .admin-pagination-info{width:100%;text-align:center}
}
/* end ADMIN pagination/empty/sm-btn/icons */ /* ── PUBLIC — Pagination ─────────────────────────────────────────────────── /blog calls ->links() and had no styling: Laravel's default paginator view is Tailwind markup and no Tailwind is loaded here. Semantic tokens only,so it reads correctly in both light and dark sections. ───────────────────────────────────────────────────────────────────────── */ .pager{display:flex;align-items:center;justify-content:center;gap:6px;flex-wrap:wrap}
.pager-link{display:inline-flex;align-items:center;justify-content:center;min-width:42px;height:42px;padding:0 14px;border-radius:12px;border:1px solid var(--color-border);color:var(--color-text);font-size:.9rem;font-weight:500;text-decoration:none;transition:all .2s}
.pager-link:hover{border-color:var(--color-brand);color:var(--color-brand-text,#9A5200)}
.pager-link.is-active{background:var(--color-brand);border-color:var(--color-brand);color:#fff;font-weight:600}
.pager-link.is-disabled{opacity:.35;pointer-events:none}
.pager-gap{display:inline-flex;align-items:center;justify-content:center;min-width:28px;height:42px;color:var(--color-text-muted)}
@media(max-width:640px){
.pager-link{min-width:38px;height:38px;padding:0 10px;font-size:.85rem}
}
/* end PUBLIC pagination */ /* ── ADMIN — relationship picker ─────────────────────────────────────────── Checkbox grid for entity relationships. Names only,never raw ids. ───────────────────────────────────────────────────────────────────────── */ .rel-picker{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:8px;
  max-height:280px;overflow-y:auto;padding:12px;border:1.5px solid var(--admin-border);
  border-radius:10px;background:var(--admin-surface)}
.rel-picker-item{display:flex;align-items:center;gap:9px;padding:7px 10px;border-radius:8px;
  font-size:.85rem;font-weight:400;cursor:pointer;transition:background .15s}
.rel-picker-item:hover{background:var(--admin-bg)}
.rel-picker-item input{flex-shrink:0;margin:0}
.rel-picker-item span{color:var(--admin-text);line-height:1.35}
@media(max-width:767px){.rel-picker{grid-template-columns:1fr;max-height:220px}}

/* Screenshot thumbnails on the product form */ .admin-thumb-grid{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px}
.admin-thumb{position:relative;width:110px;border:1.5px solid var(--admin-border);border-radius:8px;overflow:hidden}
.admin-thumb img{display:block;width:100%;height:70px;object-fit:cover}
.admin-thumb label{display:flex;align-items:center;gap:5px;padding:5px 7px;font-size:.7rem;
  color:var(--admin-text-muted);background:var(--admin-bg);font-weight:400;cursor:pointer}
/* end relationship picker */

/* ── PRODUCT — CMS-driven sections ─────────────────────────────────────────
   Steps, screenshots. Uses existing tokens; no new design language. ───────────────────────────────────────────────────────────────────────── */ .step-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-2)}
.step-item{display:flex;align-items:flex-start;gap:16px}
.step-num{flex-shrink:0;width:34px;height:34px;border-radius:50%;display:grid;place-items:center;
  background:var(--color-brand);color:#fff;font-weight:700;font-size:.9rem}
.step-text{padding-top:6px;color:var(--color-text);line-height:1.6}

.shot-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:var(--space-3)}
.shot{margin:0;border-radius:var(--radius-card,16px);overflow:hidden;border:1px solid var(--color-border);
  background:var(--color-surface)}
.shot img{display:block;width:100%;height:auto}
@media(max-width:640px){
.shot-grid{grid-template-columns:1fr}
  .step-item{gap:12px}
}
/* end product CMS sections */ /* ── CASE STUDY — client quote ─────────────────────────────────────────── */ .cs-quote{margin:0;padding:var(--space-3);border-left:3px solid var(--color-brand);
  background:var(--color-surface);border-radius:0 var(--radius-card,16px) var(--radius-card,16px) 0}
.cs-quote-by{margin-top:var(--space-2);font-size:.9rem;color:var(--color-text-muted);font-style:normal}
/* end case study quote */ /* ── HERO PHOTO — optional CMS hero image on service / industry pages ──── */ .hero-photo{display:block;width:100%;max-width:320px;height:auto;border-radius:var(--radius-card,16px);
  border:1px solid var(--color-glass-border);object-fit:cover}
@media(max-width:900px){.hero-photo{max-width:100%;margin-top:var(--space-3)}}
/* end hero photo */ /* ── MOBILE OVERLAY CTA ──────────────────────────────────────────────────── .btn sets white-space:nowrap,so a long CTA label overflowed the viewport on phones (the overlay is translated off-screen,not display:none,so it still counted towards scrollWidth). ───────────────────────────────────────────────────────────────────────── */ .ov-cta{max-width:100%;white-space:normal;text-align:center;box-sizing:border-box}
/* end mobile overlay CTA */

/* ── HERO CONTENT — allow the flex item to shrink on phones ────────────────
   #hero-content sits in a flex container; the default min-width:auto stopped it shrinking below min-content,so it measured 475px inside a 360px viewport and the hero text was clipped on both sides. ───────────────────────────────────────────────────────────────────────── */ #hero-content{min-width:0;max-width:min(900px,100%)}
/* end hero content */

/* ── NARROW PHONES — collapse the last two-column grids ────────────────────
   .stat-row and .industry-grid held two columns down to 375px, where their
   content min-width made each track ~200px and the second column was clipped.
   ───────────────────────────────────────────────────────────────────────── */
@media(max-width:480px){
.stat-row{grid-template-columns:1fr}
  .industry-grid{grid-template-columns:1fr}
}
/* end narrow-phone grid collapse */ /* ── CLIENT LOGOS + DEMO ───────────────────────────────────────────────── */ .logo-strip-label{text-align:center;font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--color-text-muted);margin:0 0 var(--space-3)}
.logo-strip{display:flex;flex-wrap:wrap;gap:var(--space-3);align-items:center;justify-content:center}
.logo-item{display:flex;align-items:center;justify-content:center;min-width:150px;height:64px;
  padding:0 22px;border:1px solid var(--color-border);border-radius:12px;background:var(--color-surface);
  text-decoration:none;transition:border-color .18s}
a.logo-item:hover{border-color:var(--color-brand)}
.logo-item img{max-height:34px;max-width:118px;object-fit:contain}
.logo-name{font-size:1rem;font-weight:600;color:var(--color-text-muted)}

.demo-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center;margin-top:var(--space-2)}
.demo-more{font-size:.82rem;color:var(--color-text-muted);text-decoration:underline}
/* muted white-alpha is invisible on a light surface */
.light-section .demo-more{color:var(--c-gray-600)}
.btn-sm{padding:7px 14px;font-size:.8rem}
.demo-form{display:flex;flex-direction:column;gap:var(--space-2);max-width:560px}
@media(max-width:640px){
.logo-strip{gap:12px}
  .logo-item{min-width:120px;height:56px;padding:0 14px}
  .demo-actions{flex-direction:column;align-items:stretch}
}
/* end client logos + demo */ /* ── ADMIN DASHBOARD ─────────────────────────────────────────────────────── Follows design-system-v3's dashboard layout,built on the existing --admin-* tokens so it inherits the current theme rather than introducing a second palette. ───────────────────────────────────────────────────────────────────────── */ .dash-metrics{display:grid;grid-template-columns:repeat(6,1fr);gap:14px;margin-bottom:20px}
.dash-metric{background:var(--admin-surface);border:1px solid var(--admin-border);border-radius:14px;
  padding:16px;box-shadow:var(--shadow-admin-card,0 1px 3px rgba(0,0,0,.06));transition:transform .2s,box-shadow .2s;
  display:block;text-decoration:none;color:inherit}
.dash-metric:hover{transform:translateY(-2px);box-shadow:var(--shadow-admin-accent-hover,0 8px 24px rgba(0,0,0,.1))}
.dash-metric-icon{width:36px;height:36px;border-radius:10px;display:grid;place-items:center;margin-bottom:10px;
  background:var(--admin-badge-indigo-bg,rgba(249,134,3,.12));color:var(--admin-accent-text)}
.dash-metric-value{font-size:1.6rem;font-weight:800;letter-spacing:-.02em;color:var(--admin-text);line-height:1.15}
.dash-metric-label{font-size:.72rem;color:var(--admin-text-muted);margin-top:3px;font-weight:500}
.dash-metric-sub{font-size:.68rem;color:var(--admin-text-muted);margin-top:6px;opacity:.85}

.dash-panels{display:grid;grid-template-columns:1.1fr 1.3fr .9fr;gap:14px;margin-bottom:14px}
.dash-panels-3{display:grid;grid-template-columns:1.4fr 1fr 1fr;gap:14px;margin-bottom:14px}
.dash-panel{background:var(--admin-surface);border:1px solid var(--admin-border);border-radius:14px;padding:18px}
.dash-panel-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px;gap:10px}
.dash-panel-head h3{font-size:.85rem;font-weight:700;color:var(--admin-text);margin:0}
.dash-panel-link{font-size:.72rem;color:var(--admin-accent-text);text-decoration:none;font-weight:600;white-space:nowrap}
.dash-panel-note{font-size:.72rem;color:var(--admin-text-muted);line-height:1.5;margin-top:10px;
  padding:10px 12px;background:var(--admin-bg);border-radius:8px;border-left:3px solid var(--admin-accent)}

.dash-funnel{display:flex;flex-direction:column;gap:4px}
.dash-funnel-stage{text-align:center;padding:11px 10px;font-weight:700;font-size:.85rem;border-radius:8px;
  min-width:38%;transition:transform .15s}
.dash-funnel-stage:hover{transform:scale(1.015)}
.dash-funnel-stage span{display:block;font-size:.66rem;font-weight:400;opacity:.9;margin-top:2px}

.dash-legend{margin-top:14px;display:grid;gap:7px;font-size:.72rem}
.dash-legend-item{display:flex;align-items:center;justify-content:space-between;color:var(--admin-text)}
.dash-legend-label{display:flex;align-items:center;color:var(--admin-text-muted)}
.dash-legend-dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:7px;flex-shrink:0}

.dash-spark{width:100%;height:150px;display:block}
.dash-spark-empty{height:150px;display:grid;place-items:center;color:var(--admin-text-muted);font-size:.78rem;
  background:var(--admin-bg);border-radius:10px}

.dash-barrow{display:grid;grid-template-columns:130px 1fr 52px;align-items:center;gap:10px;margin:11px 0;
  font-size:.74rem;color:var(--admin-text)}
.dash-bar-track{height:7px;background:var(--admin-bg);border-radius:6px;overflow:hidden}
.dash-bar-fill{height:100%;border-radius:6px;background:linear-gradient(90deg,var(--admin-accent),var(--admin-accent-dark,#e07300))}

.dash-health-row{display:flex;align-items:center;gap:9px;font-size:.76rem;padding:8px 10px;border-radius:8px;
  background:var(--admin-bg);margin-bottom:7px;text-decoration:none;color:var(--admin-text)}
.dash-health-row:hover{background:var(--admin-border)}
.dash-health-icon{width:22px;height:22px;border-radius:6px;display:grid;place-items:center;flex-shrink:0;font-size:.7rem}
.dash-health-value{margin-left:auto;font-weight:700;font-size:.76rem}

.dash-list-row{display:grid;grid-template-columns:1.3fr .9fr .9fr .7fr;padding:9px 4px;
  border-bottom:1px solid var(--admin-border);font-size:.74rem;color:var(--admin-text);align-items:center}
.dash-list-row.head{color:var(--admin-text-muted);font-weight:700;font-size:.65rem;text-transform:uppercase;
  letter-spacing:.05em}
.dash-list-row:last-child{border-bottom:none}

.dash-att{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.dash-att-figure{text-align:center;flex:1;min-width:64px}
.dash-att-num{font-size:1.5rem;font-weight:800;color:var(--admin-text);line-height:1.1}
.dash-att-label{font-size:.68rem;color:var(--admin-text-muted);margin-top:2px}

.dash-empty{text-align:center;padding:26px 14px;color:var(--admin-text-muted);font-size:.78rem}

@media(max-width:1400px){ .dash-metrics{grid-template-columns:repeat(3,1fr)} }
@media(max-width:1100px){
.dash-panels,.dash-panels-3{grid-template-columns:1fr}
}
@media(max-width:767px){
.dash-metrics{grid-template-columns:repeat(2,1fr);gap:10px}
  .dash-metric{padding:13px}
  .dash-metric-value{font-size:1.3rem}
  .dash-barrow{grid-template-columns:96px 1fr 40px;font-size:.7rem}
  .dash-list-row{grid-template-columns:1.2fr 1fr;row-gap:2px}
  .dash-list-row span:nth-child(3),.dash-list-row span:nth-child(4){display:none}
}
/* end admin dashboard */ /* ── ADMIN DRAWER — picker sizing ────────────────────────────────────────── The drawer is narrower than a full page,so the relationship pickers get a smaller minimum column and a shorter scroll area than they do on a form. ───────────────────────────────────────────────────────────────────────── */ .admin-drawer .rel-picker{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));max-height:200px}
.admin-drawer .rel-picker-item{padding:6px 9px;font-size:.8rem}
.admin-drawer .rel-picker-item span{color:var(--admin-text)}
@media(max-width:767px){
.admin-drawer .rel-picker{grid-template-columns:1fr;max-height:180px}
}
/* end drawer picker sizing */ /* ── ADMIN FORM PAGE ─────────────────────────────────────────────────────── Admin forms were capped at 760px,which left roughly half a desktop screen empty. Fluid up to a cap that still keeps text inputs a comfortable length,with paired fields in two columns and long-form fields spanning both. ───────────────────────────────────────────────────────────────────────── */ .admin-form-page{max-width:1200px;width:100%}
.admin-fieldset .span-2{grid-column:1 / -1}

/* Long-form controls should not be shorter than the label above them. */ .admin-fieldset textarea.admin-form-control{min-height:96px}

@media(max-width:1023px){
.admin-fieldset.cols-2,.admin-fieldset.cols-3{grid-template-columns:1fr}
  .admin-fieldset .span-2{grid-column:auto}
}
@media(max-width:767px){
.admin-form-page{max-width:100%}
  .admin-card{padding:18px}
  .admin-form-section{margin-bottom:28px;padding-bottom:22px}
}
/* end admin form page */ /* ── ADMIN FORM — control sizing ─────────────────────────────────────────── Short controls were being stretched to the full grid column,which made a colour picker render as a 561px hairline and left the neighbouring cell looking like an accidental gap. Sizing them to their content means the remaining space reads as deliberate. ───────────────────────────────────────────────────────────────────────── */ .admin-form-control[type="color"]{
  width:64px;height:40px;padding:4px;cursor:pointer;
  border:1.5px solid var(--admin-border);border-radius:10px;background:var(--admin-input-bg)
}
.admin-form-control[type="number"]{max-width:180px}
.admin-form-control[type="date"]{max-width:220px}
select.admin-form-control{max-width:340px}

/* Checkbox rows sit flush with the field above them rather than floating. */ .admin-form-group > label[style*="display:flex"]{margin-top:2px}

@media(max-width:1023px){
select.admin-form-control{max-width:100%}
}
/* end admin form control sizing */ /* ── NAV — secondary links ───────────────────────────────────────────────── "See a Demo" and "Free Business Ideas" sit between the primary nav and the CTA. They were added without styles and inherited raw browser defaults. Quieter than a nav item,clearly not the primary CTA. ───────────────────────────────────────────────────────────────────────── */ .nav-link-alt{
  font-size:.86rem;font-weight:500;letter-spacing:-.01em;
  color:var(--color-text-dim);text-decoration:none;white-space:nowrap;
  padding:8px 12px;border-radius:100px;transition:color .2s,background .2s
}
.nav-link-alt:hover{color:var(--color-text);background:var(--color-surface-hover)}

@media(max-width:1240px){ .nav-link-alt{padding:8px 9px;font-size:.81rem} }
@media(max-width:1023px){ .nav-link-alt{display:none} }

/* ── NAV — featured card in the mega panel ───────────────────────────────── Mirrors the design's .pn-feature: a gradient-tinted card so the panel offers an action rather than only a list of links. ───────────────────────────────────────────────────────────────────────── */ .nav-col-feature{min-width:250px;max-width:290px}
.nav-feature{
  position:relative;border-radius:16px;padding:18px;
  background:linear-gradient(145deg,rgba(249,134,3,.13),rgba(249,134,3,.03));
  display:flex;flex-direction:column;gap:8px
}
.nav-feature-tag{
  align-self:flex-start;display:inline-flex;padding:4px 10px;border-radius:999px;
  background:var(--color-brand);color:#fff;font-size:.64rem;font-weight:800;
  letter-spacing:.06em;text-transform:uppercase
}
.nav-feature-title{font-size:.98rem;font-weight:700;color:var(--color-text);line-height:1.3}
.nav-feature-copy{font-size:.78rem;line-height:1.55;color:var(--color-text-muted);margin:0}
.nav-feature-link{
  margin-top:2px;font-size:.8rem;font-weight:700;color:var(--color-brand);
  display:inline-flex;align-items:center;gap:6px;text-decoration:none;transition:gap .2s
}
.nav-feature-link:hover{gap:10px}

/* ── GRADIENT BORDERS — put the existing utility to work ─────────────────── .grad-border was built in Workstream A and used in zero views. Applied only where an edge genuinely separates a surface from a dark background,rather than sprinkled across every card. ───────────────────────────────────────────────────────────────────────── */ .cta-glass{position:relative}
.cta-glass::before,.nav-feature::before{
  content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  padding:1.5px;
  -webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude
}
.cta-glass::before{background:var(--grad-border-white)}
.nav-feature::before{background:var(--grad-border-light)}
/* end nav + gradient borders */ /* ── ADMIN — accent used as text ─────────────────────────────────────────── Brand orange is a fill colour,not a text colour: on a light surface it measures ~2.4:1. Anything that renders the accent AS TEXT uses the darkened --admin-accent-text instead,which clears 4.5:1. ───────────────────────────────────────────────────────────────────────── */ .admin-inline-link{color:var(--admin-accent-text);font-weight:600;text-decoration:none}
.admin-inline-link:hover{text-decoration:underline}
.admin-drawer summary{color:var(--admin-accent-text)}
.admin-table a:not(.admin-btn){color:var(--admin-accent-text)}
/* end accent-as-text */ /* -- MEGA MENU: How We Help ------------------------------------------------ Grid and proportions from .pn-mega-inner in design-system-v3: 1fr 1.6fr 1fr,with the outcome links in the wide middle column and a footer bar spanning all three. ------------------------------------------------------------------------ */ .nav-panel-mega{width:min(1040px,calc(100vw - 48px))}
.nav-mega-inner{
  display:grid;grid-template-columns:1fr 1.6fr 1fr;
  gap:var(--space-4);padding:var(--space-4);
}
.nav-eyebrow{
  font-size:10px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;
  color:var(--color-brand);margin-bottom:10px;
}
.nav-mega-title{
  font-size:1.35rem;font-weight:800;line-height:1.2;
  color:var(--color-text);margin-bottom:8px;
}
.nav-mega-copy{
  font-size:.82rem;line-height:1.6;color:var(--color-text-muted);
  margin:0 0 var(--space-3);
}

.nav-link-grid{display:grid;grid-template-columns:1fr 1fr;gap:4px}
.nav-mega-link{
  display:flex;flex-direction:column;gap:2px;
  padding:10px 12px;border-radius:var(--radius-md);
  text-decoration:none;transition:background .18s ease;
}
.nav-mega-link:hover{background:rgba(255,255,255,.06)}
.nav-mega-link strong{font-size:.9rem;font-weight:700;color:var(--color-text)}
.nav-mega-link span{font-size:.75rem;line-height:1.45;color:var(--color-text-muted)}
.nav-mega-link:hover strong{color:var(--color-brand)}

.nav-mega-side{display:flex;flex-direction:column;gap:8px}
.nav-mini-card{
  display:block;padding:14px;border-radius:var(--radius-md);
  border:1px solid var(--color-border);background:rgba(255,255,255,.03);
  text-decoration:none;transition:border-color .18s ease,background .18s ease;
}
.nav-mini-card:hover{border-color:var(--color-brand);background:rgba(255,255,255,.06)}
.nav-mini-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:32px;border-radius:9px;margin-bottom:8px;
  background:rgba(249,134,3,.14);color:var(--color-brand);
}
.nav-mini-card strong{display:block;font-size:.88rem;font-weight:700;color:var(--color-text)}
.nav-mini-sub{display:block;font-size:.73rem;color:var(--color-text-muted);margin-top:2px}

.nav-mega-bottom{
  grid-column:1 / -1;display:flex;align-items:center;justify-content:space-between;
  flex-wrap:wrap;gap:var(--space-2);
  margin-top:var(--space-2);padding-top:var(--space-3);
  border-top:1px solid var(--color-border);
}
.nav-bottom-links{display:flex;flex-wrap:wrap;gap:var(--space-3)}
.nav-bottom-links a{
  font-size:.8rem;font-weight:600;color:var(--color-text-muted);text-decoration:none;
}
.nav-bottom-links a:hover{color:var(--color-brand)}
.nav-bottom-cta{
  display:inline-flex;align-items:center;gap:6px;
  font-size:.8rem;font-weight:700;color:var(--color-brand);text-decoration:none;
}
.nav-bottom-cta:hover{gap:10px}

@media(max-width:1120px){
.nav-mega-inner{grid-template-columns:1fr 1.4fr}
  .nav-mega-side{grid-column:1 / -1;flex-direction:row}
  .nav-mini-card{flex:1}
}
/* end mega menu */ /* mega panel positioning */ /* Centres on the viewport,not on its trigger: at 1040px the trigger-centred default pushed the left edge off-screen. */ .nav-panel-mega{
  position:fixed;top:72px;left:50%;transform:translateX(-50%);
  max-height:calc(100vh - 96px);overflow-y:auto;
}
.nav-item.has-menu .nav-panel-mega{margin-top:0}
.nav-mega-inner{align-content:start}

/* Override the generic `.nav-panel a` flex row. */ .nav-panel .nav-mega-link{
  display:flex;flex-direction:column;align-items:flex-start;
  text-align:left;gap:2px;
}
.nav-panel .nav-mini-card{display:block;text-align:left}
.nav-panel .nav-mini-card strong{display:block}
.nav-panel .nav-bottom-links a{display:inline-flex}
/* end mega panel positioning */ /* The shared --color-border is near-invisible on the panel's own dark fill,so the mini-cards read as loose text rather than cards. */ .nav-panel .nav-mini-card{
  border-color:rgba(255,255,255,.12);
  background:rgba(255,255,255,.04);
}
.nav-panel .nav-mini-card:hover{border-color:var(--color-brand)}

/* ── Service / Industry hero photo ───────────────────────────────────────── The slot always renders now: a real upload when one exists,otherwise the branded placeholder from x-media. The category icon sits over it so the page still identifies itself when the image is generic. ───────────────────────────────────────────────────────────────────────── */ .hero-photo-wrap{position:relative;display:block;width:100%;max-width:320px;margin-left:auto}
.hero-photo-wrap .hero-photo{
  display:block;width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--radius-card,16px);
  border:1px solid var(--color-border);
}
.hero-photo-mark{
  position:absolute;right:14px;bottom:14px;
  display:grid;place-items:center;width:64px;height:64px;border-radius:16px;
  background:rgba(6,6,8,.62);color:var(--color-brand);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border:1px solid rgba(255,255,255,.16);
}
@media(max-width:900px){
.hero-photo-wrap{max-width:100%;margin:var(--space-3) 0 0}
}

/* .svc-hero-mark is a fixed 132px tinted tile built to hold a single icon. When it holds a photo instead it has to become a panel,so the tile styling is dropped. :has() is the cleanest way to keep this scoped to the photo case without adding a modifier class to two templates. */ .svc-hero-mark:has(.hero-photo-wrap){
  width:auto;height:auto;max-width:360px;
  background:none;border:none;border-radius:0;padding:0;
  display:block;justify-self:end;
}

/* Stacked layout: the panel is no longer beside the copy, so end-alignment
   would shove it against the right edge. */
@media(max-width:900px){
.svc-hero-mark:has(.hero-photo-wrap){max-width:100%;justify-self:stretch}
  .hero-photo-wrap{max-width:100%;margin-left:0}
  .hero-photo-wrap .hero-photo{aspect-ratio:16/9}
}

/* ── LIGHT-SECTION TEXT COLOURS ────────────────────────────────────────────
   .on-dark variants assume a dark surface. A block partial can now render in
   either tone, so they must not survive into a light section.
   Brand-as-text uses the darkened --color-brand-text here: #F98603 on white
   measures 2.49:1, which fails AA for text. The fill colour is untouched.
   ───────────────────────────────────────────────────────────────────────── */
/* Guide colours, kept exactly: brand orange as text. See the recorded
   deviation note above. */
.light-section .eyebrow,
.light-section .eyebrow.on-dark{color:var(--color-brand)}
.light-section .serif-accent{color:var(--color-brand)}
.light-section .section-eyebrow{color:var(--color-brand)}
/* Not a design choice - white-alpha copy on a white surface is unreadable.
   These variants assume a dark surface and must not survive into light. */
.light-section .lead.on-dark,
.light-section .body.on-dark{color:var(--c-gray-600)}
/* end light-section text colours */

/* ── BACK TO TOP ───────────────────────────────────────────────────────────
   Sits above the existing bottom-right control rather than replacing it, and
   only appears once there is something to scroll back from.
   ───────────────────────────────────────────────────────────────────────── */
.to-top{
  position:fixed;right:24px;bottom:80px;z-index:500;
  width:44px;height:44px;border-radius:50%;
  display:grid;place-items:center;
  border:1px solid var(--color-border);
  background:var(--color-surface-raised);color:var(--color-text);
  cursor:pointer;opacity:0;visibility:hidden;transform:translateY(8px);
  transition:opacity .25s ease,transform .25s ease,visibility .25s;
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  box-shadow:0 4px 14px rgba(0,0,0,.28);
}
.to-top.is-visible{opacity:1;visibility:visible;transform:translateY(0)}
.to-top:hover{border-color:var(--color-brand);color:var(--color-brand)}
.to-top svg{width:18px;height:18px}
@media(max-width:600px){.to-top{right:16px;bottom:76px;width:42px;height:42px}}
@media(prefers-reduced-motion:reduce){.to-top{transition:none}}
/* end back to top */

/* An icon inside a topbar control must track the control's own colour: the
   theme button sits on a light pill, where a fixed white icon measured 1.07:1. */
.admin-topbar-link svg,.admin-topbar-link .icon{color:currentColor}
.admin-topbar-link{color:var(--admin-text)}
#menu-solutions.nav-panel{min-width:560px}
#menu-solutions .nav-col-industries{display:grid;grid-template-columns:1fr 1fr;gap:0 var(--space-2);column-fill:balance}

/* ── ADMIN: status, readiness, relationships, sticky actions ──────────────────
   Extends the existing .admin-* system rather than replacing it. Colour never
   carries meaning on its own here: every badge and readiness row also has a
   text label, and an icon where one helps. Tone-aware values come from
   ThemeService::adminTokens(), so these work in both colour schemes. */

/* Badges — green/gray/indigo already exist; these complete the semantic set. */
.admin-badge-amber { background: var(--admin-warning-bg);  color: var(--admin-warning);  border: 1px solid var(--admin-warning-border) }
.admin-badge-red   { background: var(--admin-danger-bg);   color: var(--admin-danger);   border: 1px solid var(--admin-danger-border) }
.admin-badge-blue  { background: var(--admin-info-bg);     color: var(--admin-info);     border: 1px solid var(--admin-info-border) }
.admin-badge-purple{ background: var(--admin-relation-bg); color: var(--admin-relation); border: 1px solid var(--admin-relation-border) }
.admin-badge svg   { width: 12px; height: 12px; margin-right: 5px; flex-shrink: 0 }

/* Entity status line under a page title: "Published · SEO ready · 8/9 complete" */
.admin-entity-status { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 8px }

/* Content readiness ------------------------------------------------------- */
.admin-readiness { display: flex; flex-direction: column; gap: 10px }
.admin-readiness-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px }
.admin-readiness-title { font-size: .78rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--admin-text-muted) }
.admin-readiness-count { font-size: .82rem; font-weight: 600; color: var(--admin-text) }
.admin-readiness-bar { height: 6px; border-radius: 100px; background: var(--admin-badge-gray-bg, rgba(0,0,0,.06)); overflow: hidden }
.admin-readiness-fill { height: 100%; border-radius: 100px; background: var(--admin-success); transition: width .3s ease }
.admin-readiness-fill.is-incomplete { background: var(--admin-warning) }
.admin-readiness-list { list-style: none; margin: 4px 0 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px 16px }
.admin-readiness-item { display: flex; align-items: center; gap: 8px; font-size: .82rem; color: var(--admin-text) }
.admin-readiness-item .mark { flex-shrink: 0; width: 16px; text-align: center; font-weight: 700 }
.admin-readiness-item.is-done .mark { color: var(--admin-success) }
.admin-readiness-item.is-todo .mark { color: var(--admin-warning) }
.admin-readiness-item.is-todo { color: var(--admin-text-muted) }

/* Relationship chips — human labels, never raw ids ------------------------- */
.admin-chip-set { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px }
.admin-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border-radius: 100px; font-size: .78rem; line-height: 1.4; background: var(--admin-relation-bg); color: var(--admin-relation); border: 1px solid var(--admin-relation-border) }
.admin-chip-none { font-size: .8rem; color: var(--admin-text-muted); font-style: italic }

/* Sticky form actions ----------------------------------------------------- */
.admin-sticky-actions { position: sticky; bottom: 0; z-index: 5; display: flex; align-items: center; justify-content: flex-end; gap: 12px; flex-wrap: wrap; padding: 14px 20px; margin: 24px -20px -20px; background: var(--admin-card-bg); border-top: 1px solid var(--admin-border); box-shadow: 0 -4px 14px rgba(0,0,0,.06) }
.admin-sticky-actions .spacer { margin-right: auto; font-size: .82rem; color: var(--admin-text-muted) }

@media (max-width: 640px) {
  /* Stack on small screens, and stop the bar covering the last field. */
  .admin-sticky-actions { flex-direction: column-reverse; align-items: stretch; padding: 12px 16px; margin: 20px -16px -16px }
  .admin-sticky-actions .admin-btn { width: 100%; justify-content: center }
  .admin-sticky-actions .spacer { margin-right: 0; text-align: center; padding-bottom: 4px }
  .admin-readiness-list { grid-template-columns: 1fr }
}

@media (prefers-reduced-motion: reduce) {
  .admin-readiness-fill { transition: none }
}


/* ── COMPARE BUTTON ───────────────────────────────────────────────────────
   The compare control on the products list.

   Its colours used to be written by syncCmpBar() as inline styles, hardcoded
   for a dark surface: rgba(255,255,255,.45) text with an rgba(255,255,255,.2)
   border. Inline styles beat the stylesheet, and the products list renders on
   a .light-section, so the visible desktop button was white on near-white at a
   contrast ratio of 1.05. The mobile variant escaped only because its rule
   carries !important.

   The state now lives in a class the script toggles, and the colours come from
   the surface and the brand tokens, so the control is correct on either
   surface and in either theme without JS knowing anything about colour.
   ───────────────────────────────────────────────────────────────────────── */
.cmp-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: inherit;
  font-size: .68rem;
  line-height: 1;
  padding: 6px 12px;
  border-radius: 100px;
  border: 1px solid var(--color-border);
  background: none;
  /* Inherit the section's own text colour rather than assuming a surface. */
  color: inherit;
  opacity: .72;
  cursor: pointer;
  white-space: nowrap;
  transition: opacity .15s, color .15s, background .15s, border-color .15s;
}

.cmp-btn:hover:not(:disabled) { opacity: 1 }

.cmp-btn.is-selected {
  opacity: 1;
  color: var(--color-brand-2);
  border-color: var(--color-brand-2-line);
  background: var(--color-brand-2-tint);
}

.cmp-btn:disabled { opacity: .35; cursor: not-allowed }

.cmp-btn:focus-visible {
  outline: 2px solid var(--color-brand-2);
  outline-offset: 2px;
  opacity: 1;
}

@media (max-width: 640px) {
  /* The mobile variant collapses to a single glyph, so give it a real target
     instead of the 34x28 it had. */
  .cmp-btn { min-width: 44px; min-height: 44px; font-size: .85rem }
}

@media (prefers-reduced-motion: reduce) {
  .cmp-btn { transition: none }
}

/* ── BRAND ORANGE — accessible pairing ────────────────────────────────────
   #F98603 is the brand, and on a dark surface it is comfortably readable.
   On a light surface it is not: as text on white it measures 2.39:1, and the
   Featured badge puts white text on it at 2.48:1. Both are below AA.

   The brand colour is not replaced. A darker variant of the same hue is
   introduced for the cases that carry text, and the switch happens through a
   token scoped to the light surface, so nothing in a template needs to know
   which surface it is on. Decorative uses of --color-brand are untouched.

   Selectors here mirror the specificity of the rules they correct --
   .light-section .eyebrow is 0,2,0, so a bare .eyebrow would lose to it.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  /* Same hue, dark enough to clear 4.5:1 against white and to carry white
     text itself. Matches --admin-warning, already used for this reason. */
  --color-brand-strong: #B45309;
  /* Brand-coloured text resolves to the brand by default... */
  --color-brand-text: var(--color-brand);
}

/* ...and to the accessible variant wherever the surface is light. */
.light-section,
.light-section .card {
  --color-brand-text: var(--color-brand-strong);
}

.eyebrow,
.light-section .eyebrow,
.light-section .eyebrow.on-dark { color: var(--color-brand-text) }

.serif-accent,
.light-section .serif-accent,
.light-section .card .serif-accent { color: var(--color-brand-text) }

/* White on brand orange is 2.48:1 on any surface, so this one moves outright
   rather than being scoped. */
.badge-featured { background: var(--color-brand-strong) }

/* ── NAV — secondary links ────────────────────────────────────────────────
   Two attempts before this one. Scoping to .nav.on-dark missed /privacy,
   which is the only public page that does not set that section. Inheriting
   with opacity:.72 then dimmed the text to 4.49:1 — under AA by a hair.

   The nav bar is dark on every public page, so these take the same token the
   primary nav links already use, at full strength.
   ───────────────────────────────────────────────────────────────────────── */
.nav-link-alt { color: var(--c-gray-400); opacity: 1 }
.nav-link-alt:hover,
.nav-link-alt:focus-visible { color: var(--color-text) }

/* Text sitting on the brand tint rather than on the page surface needs one
   more step: --color-brand-strong on #fee8cf is 4.22:1, just under AA for
   the 9px tag text. */
:root { --color-brand-on-tint: #8F4207 }
.outcome-tag { color: var(--color-brand-on-tint) }

/* ── SURFACE TEXT CONTRACT ────────────────────────────────────────────────
   The readability failures across the site all had one shape.

   --color-text and its variants are defined once, theme-wide, and currently
   resolve to #0f0f17 because the active palette is light. Nothing re-scopes
   them per surface. .dark-section and .light-section each set a literal
   `color`, which is why most text looked right — but any component that reads
   the token instead of inheriting got the theme value regardless of the
   surface it was painted on.

   That produced three visible bugs:
     - the How We Help mega panel: it paints a dark panel but sits outside the
       section system, so its titles and copy were #0f0f17 on #0a0a0d (1.03:1)
     - .team-name: hardcoded --c-gray-900, so black on a dark section (1.05:1)
     - the homepage pillar cards: .dark-section .card-title forces #fff, but
       .surface-card paints a *light* background, so the titles vanished (1.12:1)

   The rule from here: a surface that paints its own background also declares
   the text tokens for its subtree. Sections did this implicitly through
   `color`; now they do it through the tokens, and the two surfaces that were
   missing from the system join it.
   ───────────────────────────────────────────────────────────────────────── */

/* Surfaces that paint dark. The mega/dropdown panels are dark surfaces that
   live outside the section rhythm, which is why they were never covered. */
.dark-section,
.nav-panel {
  --color-text:        var(--c-white);
  --color-text-dim:    rgba(255, 255, 255, .85);
  --color-text-muted:  rgba(255, 255, 255, .65);
  --color-text-subtle: rgba(255, 255, 255, .55);
  --color-text-faint:  rgba(255, 255, 255, .45);
  --color-border:      rgba(255, 255, 255, .12);
}

.nav-panel { color: var(--color-text) }

/* Surfaces that paint light. .light-section already set a literal colour;
   this makes the tokens agree with it. */
.light-section {
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  --color-text-faint:  rgba(15, 15, 23, .45);
  --color-border:      rgba(15, 15, 23, .12);
}

/* A light card inside a dark section paints its own surface, so its text
   follows the card and not the section. Without this the section-level white
   override wins and the card reads as blank. */
.dark-section .surface-card {
  /* The ground lives here, with the text contract that depends on it. A
     token would follow the section and put us straight back where we were. */
  background:          var(--c-white);
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  --color-text-faint:  rgba(15, 15, 23, .45);
  --color-border:      rgba(15, 15, 23, .12);
  color: var(--color-text);
}

.dark-section .surface-card .card-title { color: var(--color-text) }
.dark-section .surface-card .body       { color: var(--color-text-muted) }

/* Components that hardcoded a light-theme value rather than following the
   surface they sit on. */
.dark-section .team-name { color: var(--color-text) }

/* White on the brand orange is 2.49:1, the same pairing already corrected on
   the Featured badge. */
.nav-feature-tag { background: var(--color-brand-strong) }

/* ── MEGA MENU: shared side-column list ───────────────────────────────────
   How We Help and Solutions now use the same panel skeleton
   (.nav-panel-mega > .nav-mega-inner > lead / mid / side / bottom).

   The side column differs only in what it holds: How We Help points at two
   destinations, so it uses .nav-mini-card; Solutions carries eleven sectors,
   which as cards would be taller than the panel. This is the compact variant
   of the same slot.
   ───────────────────────────────────────────────────────────────────────── */
.nav-mini-list { display: flex; flex-direction: column; gap: 2px }

.nav-mini-link {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 8px;
  font-size: .84rem;
  line-height: 1.3;
  color: var(--color-text-dim);
  text-decoration: none;
  transition: background .15s, color .15s;
}
.nav-mini-link:hover,
.nav-mini-link:focus-visible { background: var(--color-surface-hover); color: var(--color-text) }
.nav-mini-link svg { flex-shrink: 0; opacity: .7 }

@media (max-width: 1120px) {
  /* .nav-mega-side becomes a row at this width, so the list wraps to chips
     rather than stretching one item per column. */
  .nav-mini-list { flex-direction: row; flex-wrap: wrap; gap: 6px }
  .nav-mini-link {
    padding: 6px 12px;
    border: 1px solid var(--color-border);
    border-radius: 100px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-mini-link { transition: none }
}

/* ── PRODUCT CARDS ────────────────────────────────────────────────────────
   Replaces the full-width .product-row list. Cards sit on a responsive grid
   and hold their proportions, so a product with a long tagline does not make
   its row taller than the rest.
   ───────────────────────────────────────────────────────────────────────── */
.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-lg);
}

.product-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Square-ish rather than a hard 1:1, so the content decides the last few
     pixels instead of being clipped or floated in dead space. */
  min-height: 300px;
  padding: var(--space-lg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-card, 16px);
  background: var(--color-surface-raised);
  text-decoration: none;
  transition: border-color .18s, transform .18s, box-shadow .18s;
}
.product-card:hover { border-color: var(--color-brand-line, var(--color-border)) }
.product-card:focus-visible { outline: 2px solid var(--color-brand-2); outline-offset: 3px }

.product-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px }

.product-card .product-name { font-size: 1.05rem; font-weight: 700; margin: 0; line-height: 1.25 }

.product-card-desc {
  font-size: .88rem;
  line-height: 1.6;
  font-weight: 350;
  margin: 0;
  color: var(--color-text-muted);
}

.product-card-tags { display: flex; flex-wrap: wrap; gap: 6px }

/* Pinned to the bottom so every card's footer lines up across the row. */
.product-card-foot {
  margin-top: auto;
  padding-top: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}
.product-card-more {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .85rem; font-weight: 600; color: var(--color-brand-2);
}

@media (max-width: 560px) {
  .product-grid { grid-template-columns: 1fr }
  .product-card { min-height: 0 }
}

@media (prefers-reduced-motion: reduce) {
  .product-card { transition: none }
}

/* ── CENTRED HEROES ───────────────────────────────────────────────────────
   Products, Services and Our Work share one hero treatment. The service and
   product detail heroes are two-column (copy + mark) and are left as they are;
   this is the listing hero.
   ───────────────────────────────────────────────────────────────────────── */
.hero-centered,
.hero-centered .section-head { text-align: center }
.hero-centered .section-head { margin-left: auto; margin-right: auto }
.hero-centered .lead,
.hero-centered .section-desc { margin-left: auto; margin-right: auto; max-width: 640px }
.hero-centered .svc-hero-actions,
.hero-centered .hero-actions { justify-content: center }
.hero-centered .eyebrow { justify-content: center }

/* .product-card-more sat on --color-brand-2, which is the orange on this
   theme and measured 2.52:1 against the light card. --color-brand-text is the
   surface-aware pairing already used elsewhere for exactly this. */
.product-card-more { color: var(--color-brand-on-tint) }

/* ── HOMEPAGE HERO ────────────────────────────────────────────────────────
   Two problems, one cause. The rotating phrase runs from fourteen characters
   to twenty-two, and the heading was set large enough that the swing changed
   the line count -- so the hero height moved on every rotation.

   The first attempt pinned the phrase with white-space:nowrap and a 22ch
   floor. That held the height but pushed the phrase far wider than the two
   lines above it, which is what made the heading look lopsided.

   Sizing the type so the longest phrase fits the measure solves both: every
   phrase sits on one line inside the same width, so the composition is even
   and the height cannot move. The reserved line below is a safety net for a
   longer phrase added later.
   ───────────────────────────────────────────────────────────────────────── */
#hero-phrase {
  display: block;
  min-height: 1.12em;
}

/* Give the scroll cue real separation from the buttons and the orbit, and a
   consistent resting place rather than whatever the flow left over. */
.hero-scroll {
  margin-top: var(--space-2xl);
  padding-top: var(--space-md);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  opacity: .75;
}

@media (max-width: 640px) {
  /* At this size the phrase may still wrap; reserve the second line so the
     heading stays put. */
  #hero-phrase { min-height: 2.24em }
  .hero-scroll { margin-top: var(--space-xl) }
}

/* The rotator now carries phrases from fourteen characters up to forty-five
   ("simplifying healthcare booking and management"), so no amount of sizing
   keeps them all on one line. Reserving the tallest case is what actually
   holds the hero still; a little space under the short phrases is a far better
   trade than a heading that moves every few seconds.

   Measured maxima: about 3.4em of heading at every desktop width, and 6.6em
   once the type stops scaling below 640px. */
.hero-h1 {
  min-height: 3.4em;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

@media (max-width: 640px) {
  .hero-h1 { min-height: 6.9em }
}

/* .section-eyebrow is the same brand-on-light pairing already corrected for
   .eyebrow, and was missed because it is a separate class. On a light section
   it measured 2.27:1 across every product page in both schemes.
   Specificity matches the rule it supersedes (.light-section .section-eyebrow). */
.section-eyebrow,
.light-section .section-eyebrow { color: var(--color-brand-text) }


/* The orbit tooltip is a child of the dark hero section, so it inherited the
   dark-surface tokens -- but it paints its own near-white glass panel
   (--cta-glass-solid is rgba(255,255,255,.92)). That left white text on a
   white card. Same rule as a light card inside a dark section: a surface that
   paints its own background declares its own tokens. */
#orbit-tooltip {
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  color: var(--color-text);
}
#orbit-tooltip > div:last-child { color: var(--color-brand-on-tint) }

/* .industry-tile paints --color-surface-raised, which is light, but it had
   four .light-section rules and none for .dark-section. On the "Explore more"
   band of an industry page -- a dark section -- it therefore kept its light
   background while inheriting the section's white text and white icons, so
   the tiles read as blank.

   Same rule as a light card inside a dark section: the tile paints its own
   surface, so it declares its own text tokens. */
.dark-section .industry-tile {
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  color: var(--color-text);
}
.dark-section .industry-tile .industry-name { color: var(--color-text) }
.dark-section .industry-tile svg { color: var(--color-text-muted) }

/* ── ADMIN: state cell (readiness + status) ───────────────────────────────
   Two columns became one. Readiness is a count, so it stays text; status is a
   state, so it is a pill with its own fill and border. Neither relies on
   colour alone -- the count is literal and the pill is worded.
   ───────────────────────────────────────────────────────────────────────── */
.admin-state { display: inline-flex; align-items: center; gap: 8px; white-space: nowrap }

.admin-state-ready {
  font-size: .78rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.admin-state-ready.is-complete { color: var(--admin-success) }
.admin-state-ready.is-partial  { color: var(--admin-warning) }

.admin-state-pill {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 100px;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .02em;
  border: 1px solid transparent;
}
.admin-state-pill--published { background: var(--admin-success-bg);  color: var(--admin-success);  border-color: var(--admin-success-border) }
.admin-state-pill--attention { background: var(--admin-warning-bg);  color: var(--admin-warning);  border-color: var(--admin-warning-border) }
.admin-state-pill--blocked   { background: var(--admin-danger-bg);   color: var(--admin-danger);   border-color: var(--admin-danger-border) }
.admin-state-pill--info      { background: var(--admin-info-bg);     color: var(--admin-info);     border-color: var(--admin-info-border) }
.admin-state-pill--draft     { background: var(--admin-badge-gray-bg); color: var(--admin-badge-gray-text); border-color: var(--admin-badge-gray-border) }

/* ── ADMIN: grouped action control ────────────────────────────────────────
   View / Edit / Delete as one segmented control rather than three buttons.
   Colour distinguishes them; the icon carries the meaning; the accessible
   name and tooltip carry it for anyone the icon does not reach.
   ───────────────────────────────────────────────────────────────────────── */
.admin-actions {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--admin-border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--admin-card-bg);
}
.admin-action-form { display: flex; margin: 0 }

.admin-action {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Comfortably clickable without turning the column into a toolbar. */
  min-width: 34px;
  height: 30px;
  padding: 0 9px;
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--admin-text-muted);
  transition: background .15s, color .15s;
}
/* Hairlines between segments, so it reads as one control. */
.admin-action + .admin-action,
.admin-action-form + .admin-action,
.admin-action-form .admin-action { border-left: 1px solid var(--admin-border) }
.admin-actions > * + * .admin-action { border-left: 1px solid var(--admin-border) }

.admin-action--view:hover   { background: var(--admin-info-bg);     color: var(--admin-info) }
.admin-action--edit         { color: var(--admin-accent-text) }
.admin-action--edit:hover   { background: var(--admin-badge-indigo-bg); color: var(--admin-accent-text) }
/* Delete stays quiet until aimed at: destructive should never draw the eye
   first in a row of routine actions. */
.admin-action--delete:hover { background: var(--admin-danger-bg);   color: var(--admin-danger) }

.admin-action:focus-visible {
  outline: 2px solid var(--admin-accent);
  outline-offset: -2px;
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  .admin-action { transition: none }
}

/* ── ADMIN: multi-select dropdown ─────────────────────────────────────────
   A checkbox group folded into a disclosure. Long relationship lists were
   taking a screen of height each in the drawer for something usually left
   alone; collapsed, the form reads as fields rather than as a wall of boxes.
   ───────────────────────────────────────────────────────────────────────── */
.admin-multiselect { position: relative }

.admin-multiselect-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 14px;
  border: 1.5px solid var(--admin-input-border, var(--admin-border));
  border-radius: 10px;
  background: var(--admin-input-bg);
  font-size: .9rem;
  color: var(--admin-text);
  cursor: pointer;
  list-style: none;
  transition: border-color .18s;
}
.admin-multiselect-summary::-webkit-details-marker { display: none }
.admin-multiselect-summary:hover { border-color: var(--admin-border-hover, var(--admin-border)) }
.admin-multiselect-summary:focus-visible {
  outline: 2px solid var(--admin-accent);
  outline-offset: 2px;
}

.admin-multiselect-count { color: var(--admin-text-muted) }
.admin-multiselect[open] .admin-multiselect-count { color: var(--admin-text) }
.admin-multiselect-caret { flex-shrink: 0; transition: transform .18s }
.admin-multiselect[open] .admin-multiselect-caret { transform: rotate(180deg) }

.admin-multiselect-panel {
  margin-top: 6px;
  padding: 8px;
  max-height: 230px;
  overflow-y: auto;
  border: 1px solid var(--admin-border);
  border-radius: 10px;
  background: var(--admin-card-bg);
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 2px;
}

.admin-multiselect-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 9px;
  border-radius: 8px;
  font-size: .85rem;
  font-weight: 400;
  cursor: pointer;
}
.admin-multiselect-item:hover { background: var(--admin-hover-bg) }
.admin-multiselect-item input { accent-color: var(--admin-accent); flex-shrink: 0 }

@media (max-width: 640px) {
  .admin-multiselect-panel { grid-template-columns: 1fr }
}

@media (prefers-reduced-motion: reduce) {
  .admin-multiselect-caret, .admin-multiselect-summary { transition: none }
}

/* ── ADMIN: wider drawer, paired fields ───────────────────────────────────
   The drawer was 620px and flattened every .cols-2 fieldset to a single
   column, so paired fields could not pair and the case study form ran to a
   very long scroll. Widening it to 860px lets the pairs sit side by side,
   which is what the fieldsets were written for.

   The single-column rule is kept for genuinely narrow viewports, and cols-3
   stays flattened -- three across is too tight even at this width.
   ───────────────────────────────────────────────────────────────────────── */
.admin-drawer { width: 860px }

@media (min-width: 900px) {
  .admin-drawer .admin-fieldset.cols-2 { grid-template-columns: 1fr 1fr }
}

/* ---- admin token aliases -------------------------------------------------
   These six were used across the admin -- --admin-muted alone in 30 places --
   but declared nowhere, so every var() fell through to nothing: transparent
   panels and unstyled text. They are aliases rather than new colours, because
   each one is an older name for a token that already exists. Aliasing keeps
   them live through the theme switch, since ThemeService re-emits the targets
   per theme and var() resolves at use time.                                 */
:root {
  --admin-surface:        var(--admin-card-bg);
  --admin-surface-2:      var(--admin-bg);
  --admin-muted:          var(--admin-text-muted);
  --admin-text-dim:       var(--admin-text-muted);
  --admin-badge-green:    var(--admin-success);
  --admin-badge-green-bg: var(--admin-success-bg);
}

/* ---- admin icon picker --------------------------------------------------- */
.admin-iconpick { position: relative; }

.admin-iconpick-trigger {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 12px; min-height: 42px;
  background: var(--admin-bg); color: var(--admin-text);
  border: 1px solid var(--admin-border); border-radius: 8px;
  font: inherit; font-size: .88rem; text-align: left; cursor: pointer;
}
.admin-iconpick-trigger:hover { border-color: var(--admin-accent); }
.admin-iconpick-trigger:focus-visible { outline: 2px solid var(--admin-accent); outline-offset: 2px; }
.admin-iconpick-trigger .icon:last-child { margin-left: auto; opacity: .6; }

.admin-iconpick-preview {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0; font-size: 1.1rem; line-height: 1;
}
.admin-iconpick-name { font-weight: 500; }
.admin-iconpick-name.is-legacy { color: var(--admin-text-muted); font-weight: 400; font-style: italic; }

.admin-iconpick-panel {
  position: absolute; z-index: 60; top: calc(100% + 6px); left: 0; right: 0;
  background: var(--admin-card-bg); border: 1px solid var(--admin-border);
  border-radius: 10px; box-shadow: var(--admin-shadow-pop), 0 16px 40px rgba(15,15,26,.18);
  padding: 10px;
}
/* Flipped by JS when the field sits too low for the panel to open downwards. */
.admin-iconpick-panel.is-above { top: auto; bottom: calc(100% + 6px); }
.admin-iconpick-panel[hidden] { display: none; }
.admin-iconpick-search { margin-bottom: 8px; }
.admin-iconpick-scroll { max-height: 232px; overflow-y: auto; overscroll-behavior: contain; }

.admin-iconpick-grouplabel {
  margin: 8px 0 6px; font-size: .68rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--admin-text-muted);
}
.admin-iconpick-group[hidden] { display: none; }
.admin-iconpick-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(78px, 1fr)); gap: 4px; }

.admin-iconpick-option {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 8px 4px; min-height: 62px;
  background: transparent; color: var(--admin-text);
  border: 1px solid transparent; border-radius: 8px;
  font: inherit; cursor: pointer;
}
.admin-iconpick-option[hidden] { display: none; }
.admin-iconpick-option span {
  font-size: .62rem; color: var(--admin-text-muted);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-iconpick-option:hover { background: var(--admin-hover-bg); border-color: var(--admin-border-hover); }
.admin-iconpick-option:focus-visible { outline: 2px solid var(--admin-accent); outline-offset: -2px; }
/* Selection is border + tint + a bolder label, never colour alone. */
.admin-iconpick-option.is-selected {
  border-color: var(--admin-accent); background: var(--admin-accent-glow);
  box-shadow: 0 0 0 1px var(--admin-accent-ring);
}
.admin-iconpick-option.is-selected span { color: var(--admin-text); font-weight: 600; }

.admin-iconpick-empty { padding: 16px 4px; text-align: center; font-size: .82rem; color: var(--admin-text-muted); }
.admin-iconpick-foot { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding-top: 8px; border-top: 1px solid var(--admin-border); margin-top: 8px; }
.admin-iconpick-count { font-size: .72rem; color: var(--admin-text-muted); }

@media (max-width: 600px) {
  .admin-iconpick-grid { grid-template-columns: repeat(auto-fill, minmax(64px, 1fr)); }
  .admin-iconpick-scroll { max-height: 200px; }
}

/* ---- drawer validation --------------------------------------------------- */
/* Shown when a save was rejected and the drawer has been reopened with the
   submitted values still in it. Carries text as well as colour. */
.admin-drawer-alert {
  margin: 0 0 18px; padding: 12px 14px;
  background: var(--admin-danger-bg); color: var(--admin-danger);
  border: 1px solid var(--admin-danger-border); border-left-width: 3px;
  border-radius: 8px; font-size: .84rem; font-weight: 500; line-height: 1.5;
}
.admin-form-control[aria-invalid="true"] { border-color: var(--admin-danger); }
.admin-form-control[aria-invalid="true"]:focus-visible { outline-color: var(--admin-danger); }

/* ---- eyebrow on dark surfaces -------------------------------------------
   .section-eyebrow was given --color-brand-text unconditionally. That token
   is the brand darkened for legibility ON LIGHT, so on a dark section it put
   dark orange on near-black: 2.84:1, under AA. The eyebrow also paints its
   own 10% brand tint, which barely lifts the near-black behind it.

   Dark surfaces take the full-strength brand instead. Specificity (0,2,0)
   matches .light-section .section-eyebrow and this rule comes later, so the
   dark case wins without touching the light one.                          */
.dark-section .section-eyebrow { color: var(--color-brand) }

/* ---- mega menu proportions ----------------------------------------------
   The Solutions panel read as cramped and empty at once. The middle column
   was 1.6fr split into two 190px halves, so every product tagline wrapped
   mid-phrase ("Know where every project actually / stands"), while the tall
   guided card in the left column stretched the row and left ~150px of dead
   space under Products and By Industry.

   Widen the middle column so a tagline fits, give the two product halves a
   real column gap, and open up the vertical rhythm in both lists so the
   shorter columns fill more of the height the left column sets.          */
.nav-mega-inner { grid-template-columns: .95fr 2fr 1.05fr; align-items: start }

.nav-link-grid { gap: 10px 18px }
.nav-mega-link { padding: 9px 12px }
.nav-mega-link span { line-height: 1.5 }

/* The sector list carries eleven items; a little more room per row reads as
   a list rather than a block of text. */
.nav-mini-list { display: flex; flex-direction: column; gap: 3px }
.nav-mini-link { padding: 5px 8px; border-radius: 8px }

/* The guided card was the tallest thing in the panel and set the row height
   for everything else. Tightened, not removed -- it is the entry point to the
   solution finder. */
.nav-feature { padding: 15px; gap: 6px }
.nav-feature-copy { font-size: .75rem; line-height: 1.5 }

/* Matches the existing 1120px fallback. The base rule above is later in the
   stylesheet than that media block, so without repeating it here the three
   columns would survive past the breakpoint. */
@media (max-width: 1120px) {
  .nav-mega-inner { grid-template-columns: 1fr 1.4fr }
}

/* One column, not two.
   Six products in a 2x3 grid came to 184px against a 390px row, so most of
   the panel under Products was empty while every tagline wrapped mid-phrase.
   A single column in the widened middle gives each tagline a full line and
   brings the column height close to the other two, which is what removes the
   dead space rather than just moving it around. */
.nav-link-grid { grid-template-columns: 1fr; gap: 2px }

/* Final proportions.
   Narrowing the lead column made its heading and card wrap more, so it grew
   to 390px and went on setting the row height. Width goes back to the lead,
   taken from the middle column, which no longer needs 431px now that the
   product links are a single column. */
.nav-mega-inner { grid-template-columns: 1.25fr 1.6fr 1.15fr }
.nav-mini-list { gap: 4px }
.nav-mini-link { padding: 6px 8px }

/* .nav-panel already pads the panel; .nav-mega-inner padded it a second time,
   which is where most of the band above the footer bar came from. */
.nav-mega-inner { padding: 8px }

/* ---- product gallery captions -------------------------------------------
   Gallery images are named for what they show (prescivo-dashboard.png), so
   the filename already carries a usable label. Showing it saves the viewer
   working out what they are looking at, and it is the same string used as the
   alt text. */
.shot-caption {
  margin-top: 10px; font-size: .82rem; line-height: 1.4;
  color: var(--color-text-muted); text-align: center;
}

/* ---- product matrix ------------------------------------------------------
   The product's facts as a specification table. Rows with no data are not
   rendered at all, so this never shows an empty field.

   Scoped to the two section surfaces rather than painting its own, so it
   inherits the correct text tokens instead of declaring a third surface.   */
.product-matrix { max-width: 820px; margin: 0 auto; }

.matrix-table { width: 100%; border-collapse: collapse; }

.matrix-table th,
.matrix-table td {
  padding: 16px 0; text-align: left; vertical-align: top;
  border-top: 1px solid var(--color-border);
}
.matrix-table tr:first-child th,
.matrix-table tr:first-child td { border-top: 0; }

.matrix-table th {
  width: 190px; padding-right: 24px;
  font-size: .74rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--color-text-muted);
  white-space: nowrap;
}
.matrix-table td { font-size: .95rem; color: var(--color-text); }

.matrix-links { display: flex; flex-wrap: wrap; gap: 8px; }
.matrix-links a,
.matrix-table td > a {
  color: var(--color-brand-text); text-decoration: none; font-weight: 500;
}
.matrix-links a {
  padding: 4px 11px; border-radius: 100px;
  border: 1px solid var(--color-border); font-size: .84rem;
}
.matrix-links a:hover { border-color: var(--color-brand); }
.matrix-table td > a:hover { text-decoration: underline; }

/* On a dark section the brand needs its full strength, the same correction
   the section eyebrow needed. */
.dark-section .matrix-links a,
.dark-section .matrix-table td > a { color: var(--color-brand); }

.matrix-features {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px 20px;
}
.matrix-features li { display: flex; align-items: flex-start; gap: 9px; font-size: .92rem; }
.matrix-features .icon { flex-shrink: 0; margin-top: 3px; color: var(--color-brand); }

@media (max-width: 640px) {
  .matrix-table th,
  .matrix-table td { display: block; width: auto; padding-right: 0; }
  .matrix-table th { border-top: 1px solid var(--color-border); padding-bottom: 6px; }
  .matrix-table td { border-top: 0; padding-top: 0; }
  .matrix-features { grid-template-columns: 1fr; }
}

/* ---- profile fact tables -------------------------------------------------
   The admin consumes the --admin-* namespace, so these do not reuse the
   public .matrix-table styling even though the shape is the same -- that one
   is built on --color-* and would pick up the wrong surface here. */
.admin-profile-facts { width: 100%; border-collapse: collapse; }

.admin-profile-facts th,
.admin-profile-facts td {
  padding: 13px 0; text-align: left; vertical-align: top;
  border-top: 1px solid var(--admin-border);
}
.admin-profile-facts tr:first-child th,
.admin-profile-facts tr:first-child td { border-top: 0; }

.admin-profile-facts th {
  width: 170px; padding-right: 20px;
  font-size: .72rem; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--admin-text-muted); white-space: nowrap;
}
.admin-profile-facts td { font-size: .9rem; color: var(--admin-text); }
.admin-profile-facts .matrix-links { display: flex; flex-wrap: wrap; gap: 6px; }

@media (max-width: 600px) {
  .admin-profile-facts th,
  .admin-profile-facts td { display: block; width: auto; padding-right: 0; }
  .admin-profile-facts th { border-top: 1px solid var(--admin-border); padding-bottom: 4px; }
  .admin-profile-facts td { border-top: 0; padding-top: 0; }
}

/* ---- admin sidebar scrollbar --------------------------------------------
   The nav scrolls, so the browser drew its default scrollbar down the edge of
   a dark sidebar -- a light grey track against near-black. Styled to sit in
   the sidebar rather than on top of it: no track, a faint thumb that only
   gains contrast on hover, and it stays a real scrollbar rather than being
   hidden, because the nav genuinely overflows and needs the affordance.   */
.admin-sidebar-nav { scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.18) transparent; }
.admin-sidebar-nav::-webkit-scrollbar { width: 6px; }
.admin-sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.admin-sidebar-nav::-webkit-scrollbar-thumb {
  background: rgba(255,255,255,.16); border-radius: 999px;
}
.admin-sidebar-nav:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,.28); }

/* The drawers scroll too, on a light surface. */
.admin-drawer-body { scrollbar-width: thin; scrollbar-color: var(--admin-border) transparent; }
.admin-drawer-body::-webkit-scrollbar { width: 8px; }
.admin-drawer-body::-webkit-scrollbar-track { background: transparent; }
.admin-drawer-body::-webkit-scrollbar-thumb {
  background: var(--admin-border); border-radius: 999px;
  border: 2px solid var(--admin-card-bg); background-clip: padding-box;
}
.admin-drawer-body:hover::-webkit-scrollbar-thumb { background: var(--admin-border-hover); background-clip: padding-box; }

/* ---- mega menu breathing room -------------------------------------------
   Still reading tight after the rebalance: the columns had room but the rows
   inside them did not.                                                     */
.nav-mega-inner { gap: 40px; padding: 14px 10px }
.nav-link-grid { gap: 6px }
.nav-mega-link { padding: 11px 14px }
.nav-mini-list { gap: 6px }
.nav-mini-link { padding: 7px 10px }
.nav-eyebrow { margin-bottom: 14px }
.nav-mega-bottom { margin-top: 18px; padding-top: 18px }

/* ---- team cards ----------------------------------------------------------
   design-system-v3's .team-card, finally applied. The card paints its own
   white surface, so it declares its own text tokens: on a dark section the
   section-level white would otherwise win and the card would read as blank.
   Same contract .surface-card already needed.                              */
.team-card-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-3);
}

.team-card {
  --color-text:        var(--c-gray-900);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  color: var(--color-text);
}
.team-card .team-name { font-size: 16px; font-weight: 700; color: var(--color-text) }
.team-card .team-role { font-size: 13px; color: var(--color-text-subtle); margin-top: 2px }

.team-card-body { min-width: 0 }
.team-card .team-link {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 7px;
  font-size: .76rem; font-weight: 600; text-decoration: none;
  color: var(--color-brand-on-tint);
}
.team-card .team-link:hover { text-decoration: underline }

/* A single member should not be stretched across the full width. */
.team-card-grid:has(.team-card:only-child) { grid-template-columns: minmax(280px, 380px); justify-content: center }

/* ---- product media (admin) ----------------------------------------------
   The image-card and gallery-grid shapes from design-system-v3, rebuilt on
   the --admin-* namespace: the public versions are built on --color-* and
   would pull the wrong surface into the admin.                            */
.admin-media-head h3, .admin-media-head h4 {
  margin: 0 0 4px; font-size: .95rem; font-weight: 700; color: var(--admin-text);
}
.admin-media-head p {
  margin: 0 0 14px; font-size: .84rem; line-height: 1.55; color: var(--admin-text-muted);
  max-width: 70ch;
}

.admin-media-slots {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 14px;
}

.admin-media-slot {
  display: flex; flex-direction: column;
  background: var(--admin-card-bg); border: 1px solid var(--admin-border);
  border-radius: 12px; overflow: hidden; transition: box-shadow .18s ease, transform .18s ease;
}
.admin-media-slot:hover { box-shadow: var(--admin-shadow-pop); transform: translateY(-2px); }
/* A filled slot is marked by its border as well as its contents, so the
   difference survives a greyscale print or a colour-blind reader. */
.admin-media-slot.is-filled { border-color: var(--admin-accent); }

.admin-media-slot-frame {
  height: 132px; display: grid; place-items: center; padding: 14px;
  background: var(--admin-bg); border-bottom: 1px solid var(--admin-border);
}
.admin-media-slot-frame img { max-width: 100%; max-height: 100%; object-fit: contain; }
.admin-media-slot-empty { color: var(--admin-text-muted); opacity: .5; }

.admin-media-slot-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 7px; }
.admin-media-slot-title { font-size: .86rem; font-weight: 600; color: var(--admin-text); }
.admin-media-slot-name {
  font-family: ui-monospace, monospace; font-size: .72rem;
  color: var(--admin-text-muted); word-break: break-all;
}
.admin-media-input { font-size: .78rem; padding: 6px 8px; }

.admin-media-remove {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .76rem; font-weight: 500; color: var(--admin-danger); cursor: pointer;
}

.admin-gallery-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 12px;
}
.admin-gallery-item {
  margin: 0; border-radius: 12px; overflow: hidden;
  background: var(--admin-card-bg); border: 1px solid var(--admin-border);
  transition: box-shadow .18s ease, transform .18s ease;
}
.admin-gallery-item:hover { box-shadow: var(--admin-shadow-pop); transform: translateY(-3px); }
.admin-gallery-item img {
  width: 100%; height: 118px; object-fit: cover; display: block;
  background: var(--admin-bg);
}
.admin-gallery-item figcaption {
  padding: 9px 11px; display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.admin-gallery-caption {
  font-size: .78rem; font-weight: 600; color: var(--admin-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.admin-media-empty {
  padding: 18px; text-align: center; font-size: .84rem; color: var(--admin-text-muted);
  border: 1px dashed var(--admin-border); border-radius: 12px;
}
.admin-media-empty code { font-family: ui-monospace, monospace; }

@media (max-width: 600px) {
  .admin-gallery-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* ---- locations (admin) ---------------------------------------------------
   A place is a thing, not a spreadsheet row: each one is a card carrying what
   it is and which location pages describe it, so the two facts and their
   pages are read together rather than across three screens.               */
.admin-loc-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px;
}

.admin-loc-card {
  background: var(--admin-card-bg); border: 1px solid var(--admin-border);
  border-radius: 12px; padding: 16px 16px 14px;
  transition: box-shadow .18s ease, transform .18s ease;
}
.admin-loc-card:hover { box-shadow: var(--admin-shadow-pop); transform: translateY(-2px); }
/* Hidden reads as hidden without relying on the badge alone. */
.admin-loc-card.is-hidden { opacity: .62; border-style: dashed; }

.admin-loc-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.admin-loc-city { font-size: 1rem; font-weight: 700; color: var(--admin-text); }
.admin-loc-region { font-size: .78rem; color: var(--admin-text-muted); margin-top: 2px; }

.admin-loc-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.admin-loc-note {
  margin: 10px 0 0; font-size: .8rem; line-height: 1.5; color: var(--admin-text-muted);
}

.admin-loc-pages { margin-top: 13px; padding-top: 11px; border-top: 1px solid var(--admin-border); }
.admin-loc-pages-label {
  font-size: .66rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--admin-text-muted); margin-bottom: 7px;
}
.admin-loc-page {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 5px 0; font-size: .83rem;
}
.admin-loc-page a { color: var(--admin-accent-text); text-decoration: none; font-weight: 500; }
.admin-loc-page a:hover { text-decoration: underline; }

.admin-loc-columns {
  display: grid; grid-template-columns: 1.35fr 1fr; gap: 20px; margin-top: 20px; align-items: start;
}
@media (max-width: 900px) { .admin-loc-columns { grid-template-columns: 1fr; } }

/* ---- product page readability -------------------------------------------
   Four failures, all the same shape: a value chosen for a light surface used
   on a dark one.

   .eyebrow was given --color-brand-text by a rule whose selector list starts
   with a bare .eyebrow, so it applied on dark sections too. That token is the
   brand darkened to stay legible ON LIGHT; on near-black it measured 2.77:1
   and the "PRODUCT" label was effectively invisible. This is the same fix
   .section-eyebrow already needed -- the plain .eyebrow was missed.         */
.dark-section .eyebrow,
.dark-section .eyebrow.on-dark { color: var(--color-brand) }

/* .include-item hardcoded --c-gray-700 text and a --c-gray-200 rule, both
   light-theme values. On the dark "Typical uses" list that was 1.92:1 -- the
   worst on the page, and the section the report was about. The surface tokens
   follow whichever section the list is in. */
.include-item {
  color: var(--color-text);
  border-bottom-color: var(--color-border);
}
.dark-section .include-item { color: var(--color-text-dim) }

/* White on full-strength brand is 2.49:1. --color-brand-strong is the same
   hue darkened for exactly this pairing and measures 5.05:1, so the numbered
   steps and the nav count keep their brand fill and become readable. */
.step-num { background: var(--color-brand-strong) }
.nav-badge { background: var(--color-brand-strong) }

/* ---- search overlay ------------------------------------------------------
   Everything inside it is written for a dark surface -- white close button,
   white input text, white hairlines -- but the background was
   var(--color-surface), which resolves to white at runtime. Opening search
   gave white on white.

   It paints its own surface, so it declares its own text tokens rather than
   inheriting whatever section it happens to sit over. Same contract the
   nav panel and the light cards needed.                                    */
#search-ov {
  background: var(--color-ink);
  --color-text:        var(--c-white);
  --color-text-dim:    rgba(255, 255, 255, .85);
  --color-text-muted:  rgba(255, 255, 255, .65);
  --color-text-subtle: rgba(255, 255, 255, .55);
  --color-border:      rgba(255, 255, 255, .12);
  color: var(--color-text);
}
#search-ov.open { opacity: .98; pointer-events: auto; }

/* The beta badge is 10px bold white on --color-brand-2, which measures
   2.84:1 -- small text, so it needs 4.5. --color-brand-2-strong is the same
   hue darkened for a white foreground and measures 5.17:1, so the badge keeps
   its colour and becomes readable. Declared here rather than inline so the
   pairing has a name the next component can reuse. */
:root { --color-brand-2-strong: #C2410C }
.badge-beta { background: var(--color-brand-2-strong) }

/* Two more of the same shape on the services listing, where the sections
   alternate light and dark: seven taglines and two counts sat on dark
   surfaces wearing values chosen for light ones.

   .svc-card-tagline takes --color-brand-text, the brand darkened to stay
   legible on light, which measured 2.77:1 on the dark cards.
   .cat-count takes a fixed gray-500, which is 4.6:1 on white but 4.09:1 on
   dark -- under AA, and the sort of near-miss that only shows up measured. */
.dark-section .svc-card-tagline { color: var(--color-brand) }
.dark-section .cat-count { color: var(--color-text-muted) }

/* ---- solution finder choices --------------------------------------------
   Step 2 takes several answers now, so the control is a checkbox and the
   selected state has to survive on both section tones -- it was written with
   a hardcoded violet tint from before the brand changed. */
.finder-choice {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 18px; border-radius: 10px;
  border: 1.5px solid var(--color-border);
  cursor: pointer; transition: border-color .15s ease, background .15s ease;
}
.finder-choice:hover { border-color: var(--color-brand) }
.finder-choice input { width: 16px; height: 16px; accent-color: var(--color-brand); flex-shrink: 0 }
.finder-choice span { font-size: .92rem }
/* Selected reads through the border and the tint, not colour alone -- the
   checkbox itself is the unambiguous signal. */
.finder-choice.is-on { border-color: var(--color-brand); background: var(--color-brand-tint) }
.finder-choice:focus-within { outline: 2px solid var(--color-brand); outline-offset: 2px }

/* ---- gallery copy rows (admin) -------------------------------------------
   Six screenshots with six sets of copy would be six walls of form, so each
   is a collapsed row: the thumbnail and its state stay scannable, the fields
   are one click away. Rows that already have a title open by default, since
   those are the ones being edited. */
.admin-gallery-list { display: flex; flex-direction: column; gap: 10px; }

.admin-gallery-row {
  border: 1px solid var(--admin-border); border-radius: 12px;
  background: var(--admin-card-bg); overflow: hidden;
}
.admin-gallery-row[open] { border-color: var(--admin-border-hover); }

.admin-gallery-row summary {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px; cursor: pointer; list-style: none;
}
.admin-gallery-row summary::-webkit-details-marker { display: none; }
.admin-gallery-row summary:hover { background: var(--admin-bg); }
.admin-gallery-row summary:focus-visible { outline: 2px solid var(--admin-accent); outline-offset: -2px; }

.admin-gallery-row summary img {
  width: 84px; height: 54px; object-fit: cover; flex-shrink: 0;
  border-radius: 8px; background: var(--admin-bg); border: 1px solid var(--admin-border);
}
.admin-gallery-row-name { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.admin-gallery-row-name strong {
  font-size: .88rem; color: var(--admin-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-gallery-row-name code {
  font-family: ui-monospace, monospace; font-size: .7rem; color: var(--admin-text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.admin-gallery-row-state { flex-shrink: 0; }

.admin-gallery-row-body {
  padding: 4px 14px 16px; border-top: 1px solid var(--admin-border);
}

@media (max-width: 600px) {
  .admin-gallery-row summary img { width: 60px; height: 40px; }
}

/* ============================================================================
   PRODUCT GALLERY — scrolling deck (.pg) and tabbed detail (.pt)

   Shapes and depths come from design-system-v3: image-card radius, the depth
   scale, and --transition's easing. Deliberately not the reference demo's
   cubic-bezier(.9,-.2,.1,1.2), which overshoots on every card.
   ========================================================================= */

/* x-section clips its texture layer with overflow:hidden, and an ancestor
   with overflow:hidden confines a sticky child to that box -- the stage
   scrolled away instead of pinning. overflow:clip does the same clipping
   without creating a scroll container, so the stage sticks to the viewport
   as intended. Measured: stage top went from -2430 to 0. */
.pg-section { overflow: clip }

/* ---- A. the scrolling deck ---------------------------------------------- */
/* Scroll length is derived from the item count, so this works for two cards
   or nine without a magic number anywhere. One viewport per card, plus one so
   the last card is readable before the section releases. */
.pg { position: relative; min-height: calc((var(--pg-count) + 1) * 100vh); }

.pg-stage {
  position: sticky; top: 0;
  min-height: 100vh;
  display: grid; grid-template-columns: 1fr 1.15fr;
  gap: var(--space-6); align-items: center;
}

.pg-copy { max-width: 46ch; }
.pg-title { margin-bottom: var(--space-3) }
.pg-lead {
  font-size: 1.05rem; font-weight: 600; color: var(--color-brand);
  margin: 0 0 6px;
}
.pg-body { font-size: 1rem; line-height: 1.7; color: var(--color-text-muted); margin: 0 }

.pg-counter {
  display: flex; align-items: baseline; gap: 6px;
  margin: var(--space-4) 0 0;
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: .95rem;
  color: var(--color-text);
}
.pg-counter-sep { color: var(--color-text-subtle); font-weight: 400 }
.pg-counter span:last-child { color: var(--color-text-subtle); font-weight: 400 }

/* The deck itself. Cards stack in one grid cell; --o is how far each card is
   from the active one, written by JS. */
.pg-deck { position: relative; display: grid; place-items: center; min-height: 60vh; }

.pg-card {
  grid-area: 1 / 1;
  width: 100%; max-width: 640px; margin: 0;
  border-radius: var(--radius-lg);
  /* --color-surface-2 resolves to a LIGHT value at runtime, so the dark
     fallback never applied and the caption was white on near-white. A
     translucent tint over the section keeps the section's own text
     tokens correct instead of declaring a third surface. */
  background: rgba(255, 255, 255, .04);
  border: 1px solid var(--color-border);
  box-shadow: var(--depth-3);
  overflow: hidden;
  transition: transform .55s cubic-bezier(.4, 0, .2, 1),
              opacity .45s cubic-bezier(.4, 0, .2, 1),
              box-shadow .55s cubic-bezier(.4, 0, .2, 1);
  will-change: transform, opacity;
}
.pg-card img {
  display: block; width: 100%; height: auto;
  /* Aspect ratio is never forced: a tall screenshot stays tall rather than
     being cropped through the middle of the UI it is meant to show. */
}
.pg-card-caption {
  padding: 12px 16px; font-size: .82rem; font-weight: 600;
  color: var(--color-text-muted); border-top: 1px solid var(--color-border);
}

/* Positions, driven by --o. Only three layers are ever visible: more than
   that and the stack reads as clutter rather than depth. */
.pg-card[data-o="0"]  { transform: none; opacity: 1; box-shadow: var(--depth-5); z-index: 4 }
.pg-card[data-o="1"]  { transform: translate3d(3%, -5%, 0) scale(.94); opacity: .55; z-index: 3 }
.pg-card[data-o="2"]  { transform: translate3d(6%, -10%, 0) scale(.88); opacity: .28; z-index: 2 }
.pg-card[data-o="3"]  { transform: translate3d(9%, -15%, 0) scale(.82); opacity: .1;  z-index: 1 }
.pg-card[data-o="far"]  { transform: translate3d(9%, -15%, 0) scale(.82); opacity: 0; z-index: 0; pointer-events: none }
/* Cards already seen leave upwards, which is the direction of travel. */
.pg-card[data-o="past"] { transform: translate3d(-4%, -34%, 0) scale(.9); opacity: 0; z-index: 0; pointer-events: none }

@media (max-width: 1024px) {
  .pg-stage { grid-template-columns: 1fr; gap: var(--space-4); align-content: center }
  .pg-copy { max-width: none }
  .pg-deck { min-height: 46vh }
}

/* Below this the sticky deck stops earning its scroll length: a phone shows
   one card at a time anyway, so the section becomes a plain sequence. */
@media (max-width: 767px) {
  .pg { min-height: 0 }
  .pg-stage { position: static; min-height: 0; display: block }
  .pg-deck { display: flex; flex-direction: column; gap: var(--space-3); min-height: 0; margin-top: var(--space-4) }
  .pg-card { grid-area: auto; transform: none !important; opacity: 1 !important; box-shadow: var(--depth-3) !important; max-width: none }
  .pg-counter { display: none }
}

/* Same fallback whenever motion is unwelcome, or before JS has run. */
@media (prefers-reduced-motion: reduce) {
  .pg { min-height: 0 }
  .pg-stage { position: static; min-height: 0; display: block }
  .pg-deck { display: flex; flex-direction: column; gap: var(--space-3); min-height: 0; margin-top: var(--space-4) }
  .pg-card { grid-area: auto; transform: none !important; opacity: 1 !important; transition: none }
  .pg-counter { display: none }
}
html:not(.js) .pg-stage { position: static }
html:not(.js) .pg-deck { display: flex; flex-direction: column; gap: var(--space-3) }
html:not(.js) .pg-card { grid-area: auto; opacity: 1; transform: none }

/* ---- B. the tabbed detail ------------------------------------------------ */
.pt-tabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: var(--space-5);
}

.pt-tab {
  appearance: none; background: none; border: 0;
  padding: 12px 16px; margin-bottom: -1px;
  font: inherit; font-size: .92rem; font-weight: 600;
  color: var(--color-text-muted); cursor: pointer; white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: var(--transition-fast);
}
.pt-tab:hover { color: var(--color-text) }
.pt-tab:focus-visible { outline: 2px solid var(--color-brand); outline-offset: -2px; border-radius: 6px }
/* Selected is carried by weight, colour and the underline together, so it
   survives without colour perception. */
.pt-tab[aria-selected="true"] {
  color: var(--color-text); border-bottom-color: var(--color-brand);
}

.pt-panel {
  display: grid; grid-template-columns: 1fr 1.2fr;
  gap: var(--space-6); align-items: center;
}
.pt-panel[hidden] { display: none }

.pt-title { font-size: 1.5rem; font-weight: 700; margin: 0 0 var(--space-2); color: var(--color-text) }
.pt-body { font-size: 1rem; line-height: 1.7; color: var(--color-text-muted); margin: 0 0 var(--space-3) }

.pt-bullets { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px }
.pt-bullets li { display: flex; align-items: flex-start; gap: 11px; font-size: .95rem; line-height: 1.6 }
.pt-bullets .icon { flex-shrink: 0; margin-top: 3px; color: var(--color-brand) }
.pt-bullets strong { color: var(--color-text); font-weight: 700 }

.pt-shot {
  margin: 0; border-radius: var(--radius-lg); overflow: hidden;
  box-shadow: var(--depth-3); border: 1px solid var(--color-border);
}
.pt-shot img { display: block; width: 100%; height: auto }

@media (max-width: 900px) {
  .pt-panel { grid-template-columns: 1fr; gap: var(--space-4) }
  /* The screenshot leads on narrow screens: it is the thing being explained. */
  .pt-shot { order: -1 }
}

/* Seven labels will not sit on one line on a phone. A scroller keeps them one
   row and swipeable rather than reflowing into a block of buttons. */
@media (max-width: 767px) {
  .pt-tabs {
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .pt-tabs::-webkit-scrollbar { display: none }
  .pt-tab { padding: 11px 13px; font-size: .86rem }
}

/* ---- industries on narrow screens ----------------------------------------
   Fourteen sectors stacked one per column is a very long scroll on a phone.
   They become a horizontal rail instead, which is the moving-row shape that
   was asked for.

   Deliberately a swipeable rail rather than an auto-running marquee: these
   cards are links, and a link that slides away is hard to tap and needs a
   pause control to be usable at all. Scroll-snap gives the same feel, stays
   tappable, works with a keyboard, and needs no control to stop it.        */
@media (max-width: 767px) {
  .ind-grid {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Bleed to the viewport edge so the next card is visibly cut off, which
       is what tells someone there is more to swipe. */
    margin-inline: calc(var(--space-3) * -1);
    padding-inline: var(--space-3);
    padding-bottom: 4px;
    gap: var(--space-2);
  }
  .ind-grid::-webkit-scrollbar { display: none }

  .ind-card {
    flex: 0 0 78%;
    max-width: 300px;
    scroll-snap-align: start;
  }
}

/* Nothing moves on its own, so there is nothing to stop -- but if the rail
   ever gains momentum scrolling, honour the preference. */
@media (max-width: 767px) and (prefers-reduced-motion: reduce) {
  .ind-grid { scroll-behavior: auto }
}

/* ---- recently viewed -----------------------------------------------------
   A quiet shelf, not a recommendation engine. Sits on whichever section tone
   it lands in and takes its colours from the surface, so it needs no rules of
   its own for dark and light.                                             */
.recent {
  margin: var(--space-5) auto 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
}
.recent[hidden] { display: none }

.recent-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px }
.recent-title {
  font-size: .72rem; font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; color: var(--color-text-muted);
}
.recent-clear {
  appearance: none; background: none; border: 0; padding: 0;
  font: inherit; font-size: .78rem; color: var(--color-text-subtle);
  cursor: pointer; text-decoration: underline;
}
.recent-clear:hover { color: var(--color-text) }
.recent-clear:focus-visible { outline: 2px solid var(--color-brand); outline-offset: 3px; border-radius: 4px }

.recent-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px }
.recent-item {
  display: inline-block; padding: 7px 14px;
  border: 1px solid var(--color-border); border-radius: 100px;
  font-size: .85rem; text-decoration: none; color: var(--color-text);
  transition: var(--transition-fast);
}
.recent-item:hover { border-color: var(--color-brand); color: var(--color-brand) }

.recent-note { margin: 12px 0 0; font-size: .74rem; color: var(--color-text-subtle) }

/* ---- leads: reading an enquiry -------------------------------------------
   The message used to be clamped to two lines with no way to finish reading
   it. The row opens under itself instead of into a drawer: the message is
   the row's own content, and a panel over the grid would cover the list
   being scanned.                                                          */
.lead-row.is-unread { background: var(--admin-hover-bg) }
/* Unread is carried by the left edge as well as the tint, so it survives
   without colour perception. */
.lead-row.is-unread td:first-child { box-shadow: inset 3px 0 0 var(--admin-accent) }

.lead-message-cell { max-width: 240px }
.lead-message-preview {
  margin: 0; font-size: .82rem; line-height: 1.5; color: var(--admin-text-muted);
  overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

.lead-panel-row[hidden] { display: none }
.lead-panel { padding: 0 !important; background: var(--admin-bg) }
.lead-panel-inner {
  display: grid; grid-template-columns: 1.6fr 1fr; gap: 24px;
  padding: 20px 22px; border-top: 1px solid var(--admin-border);
}

.lead-panel-label {
  font-size: .66rem; font-weight: 700; letter-spacing: .09em; text-transform: uppercase;
  color: var(--admin-text-muted); margin-bottom: 8px;
}
.lead-panel-message p {
  margin: 0; font-size: .9rem; line-height: 1.65; color: var(--admin-text);
  /* An enquiry is typed prose: keep the author's line breaks. */
  white-space: pre-wrap;
}
.lead-panel-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 16px }

.lead-panel-meta { margin: 0; display: flex; flex-direction: column; gap: 9px }
.lead-panel-meta > div { display: grid; grid-template-columns: 92px 1fr; gap: 10px; align-items: baseline }
.lead-panel-meta dt {
  font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--admin-text-muted);
}
.lead-panel-meta dd {
  margin: 0; font-size: .82rem; color: var(--admin-text);
  overflow-wrap: anywhere;
}
.lead-panel-meta a { color: var(--admin-accent-text); text-decoration: none }
.lead-panel-meta a:hover { text-decoration: underline }

/* The open row's control reads as pressed. */
.admin-action--view.is-open { background: var(--admin-hover-bg); border-color: var(--admin-accent) }

@media (max-width: 900px) {
  .lead-panel-inner { grid-template-columns: 1fr; gap: 18px }
}

/* ============================================================================
   CONTACT — the two-panel layout from design-system-v3 (2)

   Form on the light panel, everything you could contact us by on the dark
   one. Replaces three loose cards stacked in a sidebar.
   ========================================================================= */
.contact-grid {
  display: grid; grid-template-columns: 1.1fr .9fr;
  gap: var(--space-5); max-width: 1100px; margin: 0 auto; align-items: start;
}

.contact-form-panel {
  background: var(--color-surface);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  box-shadow: var(--depth-3);
  /* It paints its own light surface, so it carries its own text tokens --
     the section around it is dark and would otherwise win. */
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  --color-border:      rgba(15, 15, 23, .12);
  color: var(--color-text);
}

.contact-info-panel {
  position: relative; overflow: hidden;
  background: var(--color-ink);
  border-radius: var(--radius-xl);
  padding: var(--space-5);
  /* Same reason, the other way round: a dark panel that could land in a
     light section still needs light text. */
  --color-text:        var(--c-white);
  --color-text-muted:  rgba(255, 255, 255, .65);
  --color-text-subtle: rgba(255, 255, 255, .55);
  --color-border:      rgba(255, 255, 255, .12);
  color: var(--color-text);
}
/* The warm corner glow from the reference, in the brand rather than its blue. */
.contact-info-panel::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(circle at 85% 15%, var(--color-brand-glow) 0%, transparent 55%);
  opacity: .55;
}
.contact-info-panel > * { position: relative; z-index: 1 }

.contact-info-title { font-size: 1.5rem; font-weight: 700; margin: 0 0 6px; color: var(--color-text) }
.contact-info-lead { font-size: .92rem; line-height: 1.6; color: var(--color-text-muted); margin: 0 0 var(--space-4) }

.contact-info-row { display: flex; align-items: flex-start; gap: 14px; margin-bottom: var(--space-3) }
.contact-info-icon {
  width: 40px; height: 40px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--color-brand-tint); color: var(--color-brand);
}
.contact-info-label {
  font-size: .68rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--color-text-subtle); margin-bottom: 3px;
}
.contact-info-value {
  display: block; font-size: .95rem; font-weight: 600;
  color: var(--color-text); text-decoration: none; overflow-wrap: anywhere;
}
a.contact-info-value:hover { color: var(--color-brand) }
.contact-info-address { font-weight: 500; line-height: 1.5 }
.contact-info-note { font-size: .78rem; color: var(--color-text-muted); margin-top: 3px }

.contact-info-block { margin-top: var(--space-4) }
.contact-info-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px }

.contact-social-row { display: flex; gap: 10px; margin-top: var(--space-4) }
.contact-social-dot {
  width: 38px; height: 38px; border-radius: 50%;
  display: grid; place-items: center;
  border: 1px solid var(--color-border);
  font-size: .74rem; font-weight: 700; letter-spacing: .02em;
  color: var(--color-text-muted); text-decoration: none;
  transition: var(--transition-fast);
}
.contact-social-dot:hover { border-color: var(--color-brand); color: var(--color-brand) }
.contact-social-dot:focus-visible { outline: 2px solid var(--color-brand); outline-offset: 2px }

@media (max-width: 1023px) {
  .contact-grid { grid-template-columns: 1fr; max-width: 640px }
  /* The form is what someone came to use, so it stays first. */
  .contact-info-panel { order: 2 }
}
@media (max-width: 767px) {
  .contact-form-panel, .contact-info-panel { padding: var(--space-3) }
}

/* ============================================================================
   PRODUCT STACK — the pinned deck of screenshots

   The section is deliberately tall: its extra height IS the scroll distance
   the deck advances through. The stage inside is one viewport tall and sticks
   to the top, so the cards stay centred while the page moves past them.

   No ancestor of .pstack-stage may set overflow:hidden -- that silently kills
   position:sticky. .pstack-stage clips its own rays with overflow:clip, which
   does not.
   ========================================================================= */
.pstack-section { padding-top: 0; padding-bottom: 0 }

.pstack {
  position: relative;
  /* One viewport to pin in, plus a screen's worth of travel per card. */
  height: calc(100vh + (var(--pstack-count, 3) * 62vh));
}

.pstack-stage {
  position: sticky; top: 0;
  height: 100vh; overflow: clip;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: var(--space-3);
}

/* ---- the ray field ------------------------------------------------------
   The reference used a 30 KB hand-plotted SVG of fixed cyan/yellow/pink
   strokes. This is the same effect from 18 elements in the site's own brand,
   which also means it recolours with the palette instead of fighting it. */
.pstack-rays {
  position: absolute; inset: -10% 0; z-index: 0;
  width: min(420px, 60vw); margin: 0 auto; left: 0; right: 0;
  transform: skewX(-20deg);
  opacity: .5; pointer-events: none;
  mask-image: linear-gradient(to bottom, transparent, #000 22%, #000 78%, transparent);
}
.pstack-ray {
  position: absolute; top: -60%;
  left: calc(var(--i) * 5.9%);
  width: 1.3px; height: 60%;
  border-radius: 2px;
  background: linear-gradient(to bottom, transparent, var(--color-brand), transparent);
  animation: pstack-ray-fall calc(9s + (var(--i) * 1.7s)) linear infinite;
  animation-delay: calc(var(--i) * -1.3s);
  will-change: transform;
}
/* Every third ray takes the secondary brand so the field is not one flat hue. */
.pstack-ray:nth-child(3n) { background: linear-gradient(to bottom, transparent, var(--color-brand-2), transparent) }
.pstack-ray:nth-child(5n) { opacity: .45; height: 34% }

@keyframes pstack-ray-fall {
  from { transform: translate3d(0, 0, 0) }
  to   { transform: translate3d(0, 300%, 0) }
}

.pstack-head { position: relative; z-index: 2; text-align: center; max-width: 40ch }
.pstack-title { margin: 0 }

/* ---- the deck ----------------------------------------------------------- */
.pstack-deck {
  position: relative; z-index: 1;
  width: min(760px, 92vw);
  display: flex; flex-direction: column; gap: var(--space-3);
}

.pstack-card { margin: 0; position: relative }

.pstack-card-inner {
  position: relative; z-index: 2;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  box-shadow: var(--depth-3);
  /* It paints its own surface, so it declares the text colours for its own
     subtree rather than inheriting whatever the section around it happens
     to be. */
  --color-text:       var(--c-white);
  --color-text-muted: rgba(255, 255, 255, .68);
  color: var(--color-text);
}
.pstack-card-inner img { display: block; width: 100%; height: auto }

.pstack-card-caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 26px 18px 14px;
  font-size: .85rem; font-weight: 600; letter-spacing: .01em;
  color: #fff;
  background: linear-gradient(to top, rgba(0, 0, 0, .78), transparent);
}
.pstack-card-counter {
  position: absolute; top: 12px; right: 14px;
  font-size: .7rem; font-weight: 700; letter-spacing: .1em;
  padding: 4px 9px; border-radius: 100px;
  background: rgba(0, 0, 0, .55); color: #fff;
}
.pstack-card-shadow { display: none }

.pstack-counter {
  position: relative; z-index: 2; margin: 0;
  font-size: .78rem; font-weight: 700; letter-spacing: .16em;
  color: var(--color-text-muted);
}

/* ---- the pinned behaviour, only once the script says so -----------------
   Everything above this point is the plain column that renders with no
   JavaScript and under prefers-reduced-motion. */
.pstack.is-live .pstack-deck {
  display: block;
  height: min(58vh, 470px);
}
.pstack.is-live .pstack-card {
  position: absolute; inset: 0;
  display: flex; align-items: center;
  transition: transform .85s cubic-bezier(.22, 1, .36, 1),
              opacity   .55s ease;
  will-change: transform, opacity;
}
.pstack.is-live .pstack-card-inner {
  width: 100%; max-height: 100%;
  display: flex; align-items: center; justify-content: center;
}
.pstack.is-live .pstack-card-inner img { max-height: min(58vh, 470px); object-fit: contain }

/* The offset plate behind each card, from the reference. */
.pstack.is-live .pstack-card-shadow {
  display: block; position: absolute;
  top: 10px; left: 10px; right: -10px; bottom: -10px; z-index: 1;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, .05);
  border: 1px solid var(--color-border);
}

.pstack.is-live .pstack-card[data-o="past"] { transform: translateY(-135%) rotate(-2deg); opacity: 0; z-index: 1 }
.pstack.is-live .pstack-card[data-o="0"]    { transform: translateY(0)     scale(1);    opacity: 1;  z-index: 50 }
.pstack.is-live .pstack-card[data-o="1"]    { transform: translateY(16px)  scale(.955); opacity: 1;  z-index: 49 }
.pstack.is-live .pstack-card[data-o="2"]    { transform: translateY(32px)  scale(.91);  opacity: .9; z-index: 48 }
.pstack.is-live .pstack-card[data-o="3"]    { transform: translateY(48px)  scale(.865); opacity: .6; z-index: 47 }
.pstack.is-live .pstack-card[data-o="4"]    { transform: translateY(64px)  scale(.82);  opacity: .3; z-index: 46 }
.pstack.is-live .pstack-card[data-o="far"]  { transform: translateY(72px)  scale(.8);   opacity: 0;  z-index: 45 }

@media (max-width: 767px) {
  .pstack-stage { gap: var(--space-2) }
  .pstack.is-live .pstack-deck { height: min(48vh, 340px) }
  .pstack.is-live .pstack-card-inner img { max-height: min(48vh, 340px) }
  .pstack-rays { width: 80vw }
}

@media (prefers-reduced-motion: reduce) {
  .pstack { height: auto }
  .pstack-stage { position: static; height: auto; overflow: visible; padding: var(--space-5) 0 }
  .pstack-ray { animation: none }
  .pstack-counter { display: none }
}

/* ============================================================================
   GALLERY MESH — the "A look inside" grid, from design-system-v3 (2)

   An asymmetric mosaic over a soft mesh-gradient wash, with a hover state that
   dims every tile but the one under the pointer. Replaces the even auto-fit
   grid, which gave a dashboard and a login form identical weight.
   ========================================================================= */
.mesh-wrap { position: relative; padding: var(--space-2) 0 }

.mesh-bg {
  position: absolute; inset: -80px; z-index: 0;
  filter: blur(70px); opacity: .5; pointer-events: none;
  background:
    radial-gradient(circle at 12% 20%, var(--color-brand-glow, rgba(249,134,3,.38)) 0%, transparent 42%),
    radial-gradient(circle at 88% 28%, var(--color-brand-2-tint) 0%, transparent 46%),
    radial-gradient(circle at 45% 90%, var(--color-brand-tint) 0%, transparent 50%);
}

.gallery-mesh {
  position: relative; z-index: 1;
  display: grid; grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 130px; grid-auto-flow: dense;
  gap: var(--space-2); max-width: 1240px; margin: 0 auto;
}

.mesh-tile {
  position: relative; margin: 0;
  border-radius: var(--radius-lg); overflow: hidden;
  border: 1px solid var(--color-border);
  box-shadow: var(--depth-2);
  transition: box-shadow .2s cubic-bezier(.4,0,.2,1), opacity .2s ease,
              filter .2s ease, transform .2s cubic-bezier(.4,0,.2,1);
}
/* These are UI screenshots, not photographs: cropping from the top keeps the
   header and navigation -- the part that makes a screen recognisable. */
.mesh-tile img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: top center;
  transition: transform .6s cubic-bezier(.4,0,.2,1);
}
.mesh-tile.span-2x2 { grid-column: span 2; grid-row: span 2 }
.mesh-tile.span-2x1 { grid-column: span 2; grid-row: span 1 }
.mesh-tile.span-1x2 { grid-column: span 1; grid-row: span 2 }

.mesh-tile::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(0,0,0,.6), transparent 55%);
  opacity: 0; transition: opacity .2s ease;
}
.mesh-tile-label {
  position: absolute; left: 14px; bottom: 12px; z-index: 2;
  color: #fff; font-size: .8rem; font-weight: 700;
  opacity: 0; transform: translateY(8px);
  transition: var(--transition-fast);
}
.mesh-tile:hover::after,
.mesh-tile:focus-within::after { opacity: 1 }
.mesh-tile:hover .mesh-tile-label,
.mesh-tile:focus-within .mesh-tile-label { opacity: 1; transform: translateY(0) }
.mesh-tile:hover img { transform: scale(1.08) }
.mesh-tile:hover { box-shadow: 0 0 0 1px var(--color-brand), var(--depth-4); z-index: 3 }

.gallery-mesh.is-hovering .mesh-tile:not(:hover) {
  opacity: .38; filter: saturate(.4) blur(1px); transform: scale(.98);
}

@media (max-width: 1023px) {
  .gallery-mesh { grid-auto-rows: 110px }
}
@media (max-width: 767px) {
  .gallery-mesh { grid-template-columns: repeat(2, 1fr); grid-auto-rows: 120px }
  /* At two columns a 1x2 tile is a narrow sliver, which reads as a mistake. */
  .mesh-tile.span-1x2 { grid-row: span 1 }
  /* Touch has no hover, so the label has to be there from the start. */
  .mesh-tile::after { opacity: 1 }
  .mesh-tile-label { opacity: 1; transform: none }
  .gallery-mesh.is-hovering .mesh-tile:not(:hover) { opacity: 1; filter: none; transform: none }
}

@media (prefers-reduced-motion: reduce) {
  .mesh-tile, .mesh-tile img, .mesh-tile::after, .mesh-tile-label { transition: none }
  .mesh-tile:hover img { transform: none }
  .gallery-mesh.is-hovering .mesh-tile:not(:hover) { filter: none; transform: none }
}

.mesh-tile.span-4x2 { grid-column: span 4; grid-row: span 2 }
@media (max-width: 767px) { .mesh-tile.span-4x2 { grid-column: span 2 } }

/* x-section clips its texture layer with overflow:hidden, and an ancestor with
   overflow:hidden silently disables position:sticky in the subtree. clip does
   the same clipping without creating a scroll container. Same fix as
   .pg-section above -- this is the second sticky stage on the page. */
.pstack-section { overflow: clip }

/* inset:-80px on all four sides pushed the blurred wash 80px past the viewport
   and gave the page a horizontal scrollbar. The glow only needs to bleed
   vertically; sideways it is already wider than the mosaic. */
.mesh-bg { inset: -80px 0 }

/* The stack sits directly under x-section with no x-container, so it has to
   supply its own gutter -- without it the heading ran to the screen edge on
   a 390px viewport. */
.pstack-stage { padding-inline: var(--space-2) }
.pstack-head { max-width: min(40ch, 100%) }

@media (max-width: 767px) {
  /* 92vw plus the shadow plate's 10px overhang left the deck touching the
     right edge. */
  .pstack-deck { width: 86vw }
  .pstack.is-live .pstack-card-shadow { top: 7px; left: 7px; right: 0; bottom: -7px }
}

/* Screenshots have different aspect ratios, so the cards waiting behind the
   active one peeked out by different amounts and showed slivers of unrelated
   UI. Darkening them turns those slivers into plain plates, which is what the
   stack is meant to read as. */
.pstack.is-live .pstack-card:not([data-o="0"]) .pstack-card-inner {
  filter: brightness(.32) saturate(.5);
}
.pstack.is-live .pstack-card .pstack-card-inner { transition: filter .5s ease }

/* ============================================================================
   PRODUCT STACK — two columns: screenshots left, the claim right

   Supersedes the centred single-column version above. The deck advances with
   scroll; the claim beside it holds still, so there is no text/image pairing
   to fall out of step.
   ========================================================================= */
.pstack-stage {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: var(--space-6);
  align-items: center;
  justify-content: center;
  /* Replaces the flex column centring the single-column version used. */
  flex-direction: unset;
  max-width: 1240px; margin: 0 auto;
  padding-inline: var(--space-3);
}

.pstack-media { position: relative; z-index: 1; min-width: 0 }

/* The deck no longer sizes itself against the viewport width -- it now has a
   column, and the column is what it fills. */
.pstack-deck { width: 100% }

.pstack-counter {
  margin-top: var(--space-3);
  text-align: center;
}

.pstack-copy {
  position: relative; z-index: 2;
  max-width: 46ch; min-width: 0;
  text-align: left;
}
.pstack-copy .pstack-title { margin: var(--space-2) 0 var(--space-3) }

/* The rays sat behind a centred deck. With the deck in the left column they
   belong behind it, not down the middle of the section. */
.pstack-rays { left: 0; right: auto; margin: 0; width: min(420px, 34vw) }

/* ---- the claim's own surfaces on a dark section -------------------------
   .po-block and .po-block-out were written for a light section: gray-50 with
   gray-700 text, and a translucent brand tint with near-black text. The tinted
   one over a dark ground is dark-on-dark. Both restate their own colours here
   rather than inheriting a ground they were not drawn for. */
.pstack-copy .po-block {
  background: rgba(255, 255, 255, .05);
  border-left-color: rgba(255, 255, 255, .22);
}
.pstack-copy .po-text { color: var(--color-text-muted) }

.pstack-copy .po-block-out {
  background: var(--color-brand-tint);
  border-left-color: var(--color-brand);
}
.pstack-copy .po-text-out { color: var(--c-white); font-weight: 500 }

.pstack-copy .problem-tag {
  background: rgba(255, 255, 255, .1); color: rgba(255, 255, 255, .8);
}
.pstack-copy .outcome-tag {
  background: var(--color-brand); color: var(--c-white);
}

@media (max-width: 1023px) {
  .pstack-stage {
    grid-template-columns: 1fr;
    gap: var(--space-4);
    justify-items: center;
  }
  /* The claim comes first: it is the thing being made, and the screenshots
     are what back it up. */
  .pstack-copy { order: 1; text-align: center; max-width: 60ch }
  .pstack-media { order: 2; width: 100% }
  .pstack-rays { left: 0; right: 0; margin-inline: auto; width: min(420px, 70vw) }

  /* Two stacked columns need a viewport and a half to sit in, or the deck is
     squeezed to nothing under the text. */
  .pstack-stage { height: auto; min-height: 100vh; padding-block: var(--space-4) }
}

@media (max-width: 767px) {
  .pstack-stage { padding-inline: var(--space-2); gap: var(--space-3) }
  .pstack-deck { width: 100% }
}

@media (prefers-reduced-motion: reduce) {
  .pstack-stage { display: block; max-width: 900px }
  .pstack-copy { max-width: none; margin-bottom: var(--space-4) }
}

/* The rays now live inside .pstack-media, so they anchor to the deck instead
   of to the section's left edge. */
.pstack-media { position: relative }
.pstack-rays { inset: -25% 0; left: 0; right: 0; margin-inline: auto; width: min(420px, 80%) }

/* The script refuses to go live below this width, so there is no .is-live to
   undo here -- the stage only has to stop being a full-height pinned box. */
@media (max-width: 1023px) {
  .pstack { height: auto }
  .pstack-stage { position: static; height: auto; min-height: 0; overflow: visible; padding-block: var(--space-5) }
  .pstack-counter { display: none }
}

/* ============================================================================
   PRODUCT STACK — light section, centred header over two columns

   Supersedes the dark two-column block above. Everything that assumed a dark
   ground is restated here: the ray field, the plate behind each card, the
   wash on the waiting cards, and the problem/outcome blocks, which were given
   dark surfaces and now go back to the light ones they were drawn for.
   ========================================================================= */
.pstack-stage {
  grid-template-columns: 1.15fr .85fr;
  grid-template-rows: auto 1fr;
  align-content: center;
  row-gap: var(--space-4);
}

/* Spans both columns: it titles the claim and the evidence together. */
.pstack-head {
  grid-column: 1 / -1;
  justify-self: center;
  text-align: center;
  max-width: 46ch;
  margin: 0;
}
.pstack-head .pstack-title { margin: var(--space-2) 0 0 }
.pstack-copy .pstack-title { margin: 0 }

.pstack-media, .pstack-copy { align-self: center }

/* ---- the ray field on a light ground -------------------------------------
   At the dark section's opacity these read as scratches on white. They stay
   as a faint warm drift rather than being removed: the motion is the point,
   the contrast was not. */
.pstack-section .pstack-rays { opacity: .22 }
.pstack-section .pstack-ray {
  background: linear-gradient(to bottom, transparent, var(--color-brand), transparent);
}
.pstack-section .pstack-ray:nth-child(3n) {
  background: linear-gradient(to bottom, transparent, var(--color-brand-2), transparent);
}

/* ---- the cards ----------------------------------------------------------- */
.pstack-section .pstack-card-inner {
  background: var(--c-white);
  border-color: rgba(15, 15, 23, .1);
  /* It paints its own light surface, so it carries its own text tokens -- the
     caption sits on a dark gradient and keeps its own colour regardless. */
  --color-text:       var(--c-gray-900);
  --color-text-muted: rgba(15, 15, 23, .65);
  color: var(--color-text);
}

/* The plate behind the deck was white-on-dark; on light it has to be ink. */
.pstack-section .pstack.is-live .pstack-card-shadow {
  background: rgba(15, 15, 23, .04);
  border-color: rgba(15, 15, 23, .1);
}

/* Waiting cards were darkened to stop their mismatched aspect ratios showing
   slivers of unrelated UI. On a light ground the same job is done by washing
   them out towards the page, not towards black. */
.pstack-section .pstack.is-live .pstack-card:not([data-o="0"]) .pstack-card-inner {
  filter: brightness(1.6) saturate(.12) contrast(.4);
}

/* ---- the claim's blocks go back to their light styling -------------------
   These overrides existed only because the section was dark. Restating the
   original values is clearer than deleting rules further up the file that a
   future dark variant might want back. */
.pstack-section .pstack-copy .po-block {
  background: var(--c-gray-50);
  border-left-color: var(--c-gray-300);
}
.pstack-section .pstack-copy .po-text { color: var(--c-gray-700) }

.pstack-section .pstack-copy .po-block-out {
  background: var(--color-brand-tint);
  border-left-color: var(--color-brand);
}
.pstack-section .pstack-copy .po-text-out { color: var(--c-gray-900); font-weight: 500 }

.pstack-section .pstack-copy .problem-tag {
  background: var(--c-gray-200); color: var(--c-gray-600);
}
.pstack-section .pstack-copy .outcome-tag {
  background: var(--color-brand-tint); color: var(--color-brand-text);
}

@media (max-width: 1023px) {
  .pstack-stage { grid-template-rows: none; row-gap: var(--space-3) }
  .pstack-head { max-width: 60ch }
}

/* With a 1fr second row the header was pushed to the very top of the pinned
   stage, where the fixed nav sits on top of it -- the eyebrow was covered.
   Both rows size to content and the group centres in the viewport instead. */
.pstack-stage { grid-template-rows: auto auto }

/* ============================================================================
   PRODUCT HERO LOGO — a white plate with real thickness

   The uploaded marks are background-removed PNGs drawn to sit on white. On
   the dark hero, inside a translucent orange tile, any dark ink in the
   artwork disappeared -- the logo rendered and could not be read.

   The depth is a real 3D rotation rather than a drop shadow pretending: an
   edge layer sits behind the face on the Z axis, so the plate has a side you
   can see rather than a blur underneath it.
   ========================================================================= */

/* The mark slot is a fixed 132px tile built to hold one icon. Holding a plate
   instead, it stops being a tile -- same reasoning, and same :has() scoping,
   as the hero-photo case above. */
.svc-hero-mark:has(.plogo-3d) {
  width: auto; height: auto;
  background: none; border: none; box-shadow: none; border-radius: 0;
  padding: 0;
}

.plogo-3d {
  position: relative;
  width: 190px; height: 190px;
  perspective: 1100px;
  flex-shrink: 0;
}

/* Brand light thrown on the wall behind the plate, so it sits in the hero
   rather than on top of it. */
.plogo-3d-glow {
  position: absolute; inset: -28%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--color-brand-glow, rgba(249,134,3,.38)) 0%, transparent 62%);
  filter: blur(24px); opacity: .8; pointer-events: none;
}

.plogo-3d-face,
.plogo-3d-edge {
  position: absolute; inset: 0;
  border-radius: 30px;
  transform: rotateY(-17deg) rotateX(9deg);
  transform-origin: center;
  transition: transform .5s cubic-bezier(.22, 1, .36, 1);
}

/* The side of the plate: same shape, pushed back and down, in a dimmer white
   so the two read as one solid object lit from the upper left. */
.plogo-3d-edge {
  background: linear-gradient(140deg, #cfd3da, #8f97a4);
  transform: rotateY(-17deg) rotateX(9deg) translate3d(9px, 9px, -14px);
  filter: blur(.3px);
}

.plogo-3d-face {
  display: grid; place-items: center;
  padding: 26px;
  background: linear-gradient(150deg, #ffffff 0%, #f4f6f9 62%, #e9edf3 100%);
  box-shadow:
    /* the lit top edge */
    inset 0 2px 0 rgba(255, 255, 255, .95),
    /* the shaded bottom edge */
    inset 0 -2px 6px rgba(15, 23, 42, .1),
    /* contact shadow, then the cast shadow it throws on the hero */
    0 10px 20px -8px rgba(0, 0, 0, .55),
    0 34px 60px -18px rgba(0, 0, 0, .6);
}

.plogo-3d-img {
  display: block; width: 100%; height: 100%;
  object-fit: contain;
}

/* Settling towards the viewer on hover reads as the plate being a real thing
   in the scene. It is decoration only -- nothing here is a control. */
.svc-hero-grid:hover .plogo-3d-face { transform: rotateY(-9deg) rotateX(5deg) }
.svc-hero-grid:hover .plogo-3d-edge { transform: rotateY(-9deg) rotateX(5deg) translate3d(6px, 7px, -14px) }

@media (max-width: 1023px) {
  .plogo-3d { width: 150px; height: 150px }
  .plogo-3d-face { padding: 20px; border-radius: 24px }
  .plogo-3d-edge { border-radius: 24px }
}
@media (max-width: 767px) {
  .plogo-3d { width: 124px; height: 124px }
  .plogo-3d-face { padding: 16px; border-radius: 20px }
  .plogo-3d-edge { border-radius: 20px }
}

@media (prefers-reduced-motion: reduce) {
  .plogo-3d-face, .plogo-3d-edge { transition: none }
  .svc-hero-grid:hover .plogo-3d-face,
  .svc-hero-grid:hover .plogo-3d-edge { transform: rotateY(-17deg) rotateX(9deg) }
  .svc-hero-grid:hover .plogo-3d-edge { transform: rotateY(-17deg) rotateX(9deg) translate3d(9px, 9px, -14px) }
}

/* The badges inherit the H1's tight tracking. At 10px uppercase a -1px
   letter-spacing closes the word space entirely: "Coming soon" rendered as
   COMINGSOON. Badges are labels, not display type. */
h1 .badge-beta, h1 .badge-coming-soon { letter-spacing: .04em }

/* At 9.5px bold the tag is small text, so it needs 4.5:1 -- the brand-on-tint
   pairing measured 4.22 against the composited tint. Both values are opaque
   here so the contrast is what it says it is rather than depending on what
   the tint happens to be laid over. */
.pstack-section .pstack-copy .outcome-tag { background: #fde7ce; color: #7a3e00 }
.pstack-section .pstack-copy .problem-tag { background: #e5e7eb; color: #4b5563 }

/* ============================================================================
   ADMIN — lead status cluster and detail popover

   Five one-glyph columns in the idea-series grid became two clusters. Colour
   is the quick read, but every chip also carries a distinct glyph and states
   its meaning in words on hover and to assistive technology, so nothing here
   is knowable only by hue.
   ========================================================================= */
.lead-cluster {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--admin-border);
  border-radius: 8px; overflow: hidden;
  background: var(--admin-card-bg);
}
.lead-cluster > * + * { border-left: 1px solid var(--admin-border) }

.lead-chip {
  display: inline-flex; align-items: center; justify-content: center; gap: 3px;
  /* Comfortably clickable without turning the column into a toolbar. */
  min-width: 36px; height: 30px; padding: 0 8px;
  border: 0; background: none; margin: 0;
  color: var(--admin-text-muted);
  transition: background .15s, color .15s;
}
button.lead-chip { cursor: pointer }
.lead-chip .icon { flex-shrink: 0 }

.lead-chip--green { color: #16a34a; background: rgba(34, 197, 94, .1) }
.lead-chip--amber { color: #b45309; background: rgba(251, 191, 36, .14) }
.lead-chip--muted { color: var(--admin-text-muted) }

.lead-chip--act { color: var(--admin-accent-text) }
.lead-chip--act:hover { background: var(--admin-bg, #f1f5f9) }
.lead-chip--act:active { transform: scale(.92) }
.lead-chip--act.is-open { background: var(--admin-bg, #f1f5f9); color: var(--admin-text) }
.lead-chip:focus-visible { outline: 2px solid var(--admin-accent-text); outline-offset: -2px }

/* The note count, when there is one. An empty span collapses to nothing. */
.lead-chip-count { font-size: .68rem; font-weight: 700; line-height: 1 }

/* ---- follow-up cluster --------------------------------------------------- */
.lead-followup { display: flex; align-items: center; gap: 8px }
.lead-followup .lead-chip {
  border: 1px solid var(--admin-border); border-radius: 8px;
  min-width: 32px; padding: 0 6px;
}
.lead-meet {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .76rem; color: var(--admin-text-muted); white-space: nowrap;
}
.lead-meet.is-set { color: var(--admin-text); font-weight: 500 }

/* ---- the detail popover -------------------------------------------------- */
.lead-pop {
  position: fixed; z-index: 60;
  width: min(340px, calc(100vw - 16px));
  max-height: min(70vh, 520px); overflow-y: auto;
  background: var(--admin-card-bg);
  border: 1px solid var(--admin-border);
  border-radius: 12px;
  box-shadow: 0 18px 44px rgba(0, 0, 0, .18);
  color: var(--admin-text);
}
.lead-pop[hidden] { display: none }

.lead-pop-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; border-bottom: 1px solid var(--admin-border);
  background: var(--admin-card-bg);
}
.lead-pop-title { margin: 0; font-size: .9rem; font-weight: 700 }
.lead-pop-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; flex-shrink: 0;
  border: 1px solid var(--admin-border); border-radius: 7px;
  background: none; color: var(--admin-text-muted); cursor: pointer;
}
.lead-pop-close:hover { background: var(--admin-bg, #f1f5f9); color: var(--admin-text) }

.lead-pop-body { padding: 12px 14px 14px }

.lead-pop-list { margin: 0; display: grid; gap: 7px }
.lead-pop-row { display: grid; grid-template-columns: 84px 1fr; gap: 10px; align-items: baseline }
.lead-pop-row dt {
  font-size: .68rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--admin-text-muted);
}
.lead-pop-row dd { margin: 0; font-size: .82rem; overflow-wrap: anywhere }

.lead-pop-group { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--admin-border) }
.lead-pop-group-title {
  font-size: .68rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--admin-text-muted); margin-bottom: 8px;
}

.lead-pop-social { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px }
.lead-pop-social li { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: .8rem }
.lead-pop-social a { color: var(--admin-accent-text); text-decoration: none; overflow-wrap: anywhere }
.lead-pop-social a:hover { text-decoration: underline }
.lead-pop-social em { font-style: normal; color: var(--admin-text-muted); font-size: .75rem; white-space: nowrap }

.lead-pop-notes { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px }
.lead-pop-notes li { font-size: .8rem; line-height: 1.5 }
.lead-pop-note-meta {
  display: block; font-size: .68rem; color: var(--admin-text-muted); margin-bottom: 2px;
}
.lead-pop-empty { margin: 0; font-size: .8rem; color: var(--admin-text-muted) }

@media (prefers-reduced-motion: reduce) {
  .lead-chip { transition: none }
  .lead-chip--act:active { transform: none }
}

/* The chip tones were hard-coded greens and ambers, which hold on the light
   admin and go muddy on the dark one. The admin already carries semantic
   tokens that flip with the theme -- use those. */
.lead-chip--green { color: var(--admin-success); background: var(--admin-success-bg) }
.lead-chip--amber { color: var(--admin-warning); background: var(--admin-warning-bg) }

/* ============================================================================
   ADMIN — per-screenshot copy repeater
   ========================================================================= */
.gcopy-list { display: grid; gap: 14px }

.gcopy-item {
  display: grid; grid-template-columns: 200px 1fr; gap: 16px;
  padding: 14px; border: 1px solid var(--admin-border); border-radius: 12px;
  background: var(--admin-card-bg);
}

.gcopy-shot {
  border: 1px solid var(--admin-border); border-radius: 8px; overflow: hidden;
  background: var(--admin-bg, #f1f5f9);
  align-self: start;
}
.gcopy-shot img { display: block; width: 100%; height: auto }

.gcopy-fields { min-width: 0 }

.gcopy-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.gcopy-file {
  font-size: .74rem; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--admin-text-muted); overflow-wrap: anywhere;
}

.gcopy-bullet + .gcopy-bullet { margin-top: 6px }

.gcopy-toggles { display: flex; gap: 18px; flex-wrap: wrap }
.gcopy-toggle {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .84rem; font-weight: 500; cursor: pointer;
}
.gcopy-toggle input[type="checkbox"] { width: 16px; height: 16px; margin: 0; cursor: pointer }

@media (max-width: 900px) {
  .gcopy-item { grid-template-columns: 1fr }
  .gcopy-shot { max-width: 260px }
}

.pt-intro { font-size: 1rem; line-height: 1.7; color: var(--color-text-muted); max-width: 62ch; margin: var(--space-2) auto 0 }

/* ============================================================================
   ADMIN — attendance

   Same language as the lead grids: a status carries a tone, a glyph and a
   word, never a colour on its own, and the tones come from the admin's
   semantic tokens so they hold on both themes.
   ========================================================================= */
.att-note {
  color: var(--admin-text-muted); font-size: .88rem;
  max-width: 720px; margin-bottom: 18px;
}

/* ---- the day at a glance ------------------------------------------------- */
.att-summary {
  display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 18px;
}
.att-stat {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 14px; border-radius: 10px;
  border: 1px solid var(--admin-border);
  background: var(--admin-card-bg);
}
.att-stat-icon { display: inline-flex; flex-shrink: 0 }
.att-stat-num { font-size: 1.05rem; font-weight: 700; line-height: 1 }
.att-stat-label {
  font-size: .7rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--admin-text-muted);
}

.att-stat--green .att-stat-icon, .att-stat--green .att-stat-num { color: var(--admin-success) }
.att-stat--red   .att-stat-icon, .att-stat--red   .att-stat-num { color: var(--admin-danger) }
.att-stat--amber .att-stat-icon, .att-stat--amber .att-stat-num { color: var(--admin-warning) }
.att-stat--blue  .att-stat-icon, .att-stat--blue  .att-stat-num { color: var(--admin-info) }
.att-stat--muted .att-stat-icon, .att-stat--muted .att-stat-num { color: var(--admin-text-muted) }

/* ---- status chips -------------------------------------------------------- */
.att-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px; border-radius: 100px;
  font-size: .76rem; font-weight: 600; white-space: nowrap;
  border: 1px solid transparent;
}
.att-chip .icon { flex-shrink: 0 }
.att-chip--sm { font-size: .68rem; padding: 3px 8px; margin-left: 6px }

.att-chip--green { background: var(--admin-success-bg); color: var(--admin-success); border-color: var(--admin-success-border) }
.att-chip--red   { background: var(--admin-danger-bg);  color: var(--admin-danger);  border-color: var(--admin-danger-border) }
.att-chip--amber { background: var(--admin-warning-bg); color: var(--admin-warning); border-color: var(--admin-warning-border) }
.att-chip--blue  { background: var(--admin-info-bg);    color: var(--admin-info);    border-color: var(--admin-info-border) }
.att-chip--muted { background: transparent; color: var(--admin-text-muted); border-color: var(--admin-border) }

/* ---- hours --------------------------------------------------------------- */
.att-person { font-weight: 600; color: var(--admin-text) }
.att-role   { color: var(--admin-text-muted); font-size: .82rem }

.att-hours { display: flex; align-items: center; gap: 10px; flex-wrap: wrap }
.att-time {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: .8rem; color: var(--admin-text); white-space: nowrap;
}
.att-time .icon { color: var(--admin-text-muted) }
.att-worked {
  font-size: .72rem; font-weight: 700; letter-spacing: .03em;
  padding: 2px 8px; border-radius: 6px;
  background: var(--admin-bg, #f1f5f9); color: var(--admin-text-muted);
}
.att-empty { font-size: .8rem; color: var(--admin-text-muted) }

/* ---- the correction control --------------------------------------------- */
.att-correct { display: flex; gap: 6px; align-items: center }
.att-correct .admin-form-control { padding: 5px 8px; font-size: .75rem; width: auto; min-width: 96px }
.att-corrected {
  display: block; margin-top: 4px;
  font-size: .68rem; color: var(--admin-text-muted);
}

@media (max-width: 900px) {
  .att-summary { gap: 8px }
  .att-stat { padding: 8px 11px }
}

/* ============================================================================
   CLIENT LOGO STRIP — the text fallback

   None of these businesses has a logo file, so every tile is the name. A
   fixed-height row built for a 34px mark cannot hold "Nivedita's Creation
   Designer Boutique" on one line, let alone a city under it.
   ========================================================================= */
.logo-item-text {
  flex-direction: column; justify-content: center; gap: 3px;
  height: auto; min-height: 64px; padding: 14px 18px;
  max-width: 260px; text-align: center;
}
.logo-item-text .logo-name {
  font-size: .95rem; line-height: 1.3;
  color: var(--color-text); overflow-wrap: anywhere;
}
.logo-place {
  font-size: .72rem; letter-spacing: .02em;
  color: var(--color-text-muted); line-height: 1.3;
}

@media (max-width: 767px) {
  .logo-item-text { max-width: 100%; min-height: 56px; padding: 12px 14px }
}

/* ============================================================================
   §17 REVERSED — the primary CTA becomes readable

   The deviation recorded at §17 accepted white text on the brand orange at
   2.49:1, against the 4.5:1 AA needs for text this size. The same note
   recorded the way back, and this is it: near-black on the same orange
   measures 8.14:1 and changes nothing else about the button.

   The gradient is identical on the dark hero nav and the light scrolled nav,
   so one value covers both themes rather than two rules that could drift.
   ========================================================================= */
.btn-grad,
.nav-cta { color: var(--color-ink) !important }

/* Not .badge-featured. It was moved off the brand orange onto the darker
   --color-brand-strong precisely so white would pass, and white measures
   about 4.7:1 there. Near-black on that same amber is 4.03:1, under the 4.5
   its 10px bold text needs -- a fix on one background is a regression on
   another. */

/* The shadow under the button was tuned to sit beneath white text. Against
   dark text it reads as smudge rather than lift. */
.nav-cta { box-shadow: 0 6px 16px rgba(249, 134, 3, .28) }
.nav-cta:hover { box-shadow: 0 12px 26px rgba(249, 134, 3, .38) }

/* Focus has to be visible on the orange itself, so it takes the ink rather
   than the brand it would disappear into. */
.btn-grad:focus-visible,
.nav-cta:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 2px }

/* ============================================================================
   ADMIN — reviews inside the customer form
   ========================================================================= */
.rev-list { display: grid; gap: 12px }
.rev-item {
  padding: 14px; border: 1px solid var(--admin-border);
  border-radius: 12px; background: var(--admin-card-bg);
}
.rev-toggle {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: .84rem; font-weight: 500; cursor: pointer;
}
.rev-toggle input[type="checkbox"] { width: 16px; height: 16px; margin: 0; cursor: pointer }

/* The form-section heading was an h3 sitting directly under the page h1, so
   every admin form skipped h2. The rule was written for h3 alone; it takes
   both now, and the customer form uses h2. */
.admin-form-section h2 {
  font-size: .95rem; font-weight: 700; margin-bottom: 24px;
  color: var(--admin-text); display: flex; align-items: center; gap: 8px;
}
.admin-form-section h2::before {
  content: ''; display: inline-block; width: 3px; height: 16px;
  background: var(--admin-accent); border-radius: 2px;
}

/* The logo now sits above the name rather than instead of it. */
.logo-item-text img {
  max-height: 34px; max-width: 100px; width: auto; height: auto;
  object-fit: contain; margin-bottom: 2px;
}

/* ============================================================================
   CUSTOMER MARQUEE

   Two identical runs side by side, translated by exactly half the track. At
   -50% the second run sits precisely where the first began, so the loop has
   no seam and no jump.
   ========================================================================= */
.cmq { margin-top: var(--space-5); }

.cmq-label {
  text-align: center; margin: 0 0 var(--space-3);
  font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--color-text-subtle);
}

.cmq-viewport {
  overflow: hidden;
  /* Fades the ends rather than cutting names in half at the edge. */
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}

.cmq-track {
  display: flex; width: max-content;
  animation: cmq-drift calc(var(--cmq-count, 8) * 4.5s) linear infinite;
  will-change: transform;
}
.cmq-viewport:hover .cmq-track,
.cmq-track:focus-within { animation-play-state: paused }

@keyframes cmq-drift {
  from { transform: translate3d(0, 0, 0) }
  to   { transform: translate3d(-50%, 0, 0) }
}

.cmq-run {
  display: flex; align-items: center; gap: var(--space-2);
  list-style: none; margin: 0; padding: 0 var(--space-1);
}

.cmq-item {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  height: 56px; padding: 0 20px;
  border: 1px solid var(--color-border);
  border-radius: 100px;
  background: var(--color-surface-raised);
  white-space: nowrap;
}
.cmq-item img {
  width: auto; height: 28px; max-width: 64px;
  object-fit: contain; flex-shrink: 0;
}
.cmq-name { font-size: .92rem; font-weight: 600; color: var(--color-text) }

@media (max-width: 767px) {
  .cmq-item { height: 48px; padding: 0 15px; gap: 8px }
  .cmq-item img { height: 22px; max-width: 48px }
  .cmq-name { font-size: .84rem }
}

/* A moving row of names cannot be read by someone who needs it still. */
@media (prefers-reduced-motion: reduce) {
  .cmq-viewport { mask-image: none; overflow: visible }
  .cmq-track { animation: none; width: 100%; flex-wrap: wrap; justify-content: center }
  .cmq-run { flex-wrap: wrap; justify-content: center; gap: 10px }
  .cmq-track .cmq-run + .cmq-run { display: none }
}

/* ============================================================================
   CUSTOMER ACCORDION — what each of them said

   The design system's accordion, with its own note followed: the header is a
   real button carrying aria-expanded rather than a clickable div.
   ========================================================================= */
.cacc {
  max-width: 760px; margin: 0 auto;
  display: flex; flex-direction: column; gap: var(--space-2);
}

.cacc-item {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface-raised);
  overflow: hidden;
  transition: box-shadow .2s ease, border-color .2s ease;
}
.cacc-item:has([aria-expanded="true"]) {
  box-shadow: var(--depth-3);
  border-color: var(--color-brand-line);
}

.cacc-heading { margin: 0; font-size: inherit; font-weight: inherit }

.cacc-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  width: 100%; padding: var(--space-3) var(--space-4);
  background: none; border: 0; cursor: pointer; text-align: left;
  color: var(--color-text); font: inherit;
}
.cacc-header:focus-visible { outline: 2px solid var(--color-brand); outline-offset: -3px }

.cacc-who { display: flex; align-items: center; gap: 14px; min-width: 0 }
.cacc-logo {
  width: auto; height: 34px; max-width: 56px;
  object-fit: contain; flex-shrink: 0;
}
.cacc-names { display: flex; flex-direction: column; gap: 2px; min-width: 0 }
.cacc-name { font-size: 1rem; font-weight: 700; overflow-wrap: anywhere }
.cacc-place { font-size: .76rem; color: var(--color-text-muted) }

.cacc-icon {
  display: grid; place-items: center; flex-shrink: 0;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--color-brand-tint); color: var(--color-brand);
  font-size: 1rem; font-weight: 700; line-height: 1;
  transition: transform .25s var(--ease-standard, cubic-bezier(.4,0,.2,1)), background .2s, color .2s;
}
[aria-expanded="true"] .cacc-icon {
  transform: rotate(45deg);
  background: var(--color-brand); color: var(--color-ink);
}

.cacc-panel {
  display: grid; grid-template-rows: 1fr;
  transition: grid-template-rows .3s var(--ease-standard, cubic-bezier(.4,0,.2,1));
}
/* grid-template-rows 0fr rather than max-height: the panel animates to its own
   height, whatever that is, instead of to a number guessed in advance. */
.cacc-panel[hidden] { display: grid; grid-template-rows: 0fr }
.cacc-panel > * { overflow: hidden; min-height: 0 }

.cacc-panel-inner { padding: 0 var(--space-4) var(--space-3) }

.cacc-quote { margin: 0 }
.cacc-quote + .cacc-quote { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--color-border) }
.cacc-quote blockquote {
  margin: 0 0 10px; padding: 0;
  font-size: 1rem; line-height: 1.7; color: var(--color-text-dim, var(--color-text));
}
.cacc-quote figcaption { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline }
.cacc-author { font-size: .86rem; font-weight: 600; color: var(--color-text) }
.cacc-role { font-size: .78rem; color: var(--color-text-muted) }

@media (max-width: 767px) {
  .cacc-header { padding: var(--space-2) var(--space-3) }
  .cacc-panel-inner { padding: 0 var(--space-3) var(--space-2) }
  .cacc-logo { height: 28px; max-width: 44px }
}

@media (prefers-reduced-motion: reduce) {
  .cacc-panel, .cacc-icon, .cacc-item { transition: none }
}

/* The pill paints its own light surface, so it carries its own text colours.
   Inheriting them left white names on a white pill: --color-text resolves to
   white inside a dark section, and this marquee sits in one. The same contract
   every self-painting component on this site has to honour. */
.cmq-item {
  --color-text:       var(--c-gray-900);
  --color-text-muted: rgba(15, 15, 23, .62);
  background: var(--c-white);
  border-color: rgba(15, 15, 23, .1);
  color: var(--color-text);
}
.cmq-name { color: var(--color-text) }

/* grid-template-rows: 0fr collapses the row's content box, and overflow:hidden
   clips what is inside it -- but padding is neither. A closed panel kept its
   24px of bottom padding, so every collapsed row left a gap. The padding
   collapses with the row now. */
.cacc-panel-inner {
  transition: padding .3s var(--ease-standard, cubic-bezier(.4, 0, .2, 1));
}
.cacc-panel[hidden] .cacc-panel-inner { padding-top: 0; padding-bottom: 0 }

@media (prefers-reduced-motion: reduce) {
  .cacc-panel-inner { transition: none }
}

/* ============================================================================
   ADMIN — media library
   ========================================================================= */
.mlib-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 18px }
.mlib-tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border-radius: 100px;
  border: 1px solid var(--admin-border);
  background: var(--admin-card-bg);
  color: var(--admin-text-muted);
  font-size: .82rem; font-weight: 500; text-decoration: none;
}
.mlib-tab span {
  font-size: .7rem; font-weight: 700;
  padding: 1px 7px; border-radius: 100px;
  background: var(--admin-bg, #f1f5f9);
}
.mlib-tab:hover { color: var(--admin-text) }
.mlib-tab.is-on {
  background: var(--admin-accent-text); border-color: var(--admin-accent-text); color: #fff;
}
.mlib-tab.is-on span { background: rgba(255, 255, 255, .22); color: #fff }

.mlib-grid {
  display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
}

.mlib-item {
  display: flex; flex-direction: column;
  border: 1px solid var(--admin-border); border-radius: 12px;
  background: var(--admin-card-bg); overflow: hidden;
}
.mlib-item.is-missing { border-color: var(--admin-danger) }

.mlib-thumb {
  display: grid; place-items: center;
  height: 150px; padding: 12px;
  background: var(--admin-bg, #f1f5f9);
  border-bottom: 1px solid var(--admin-border);
}
.mlib-thumb img { max-width: 100%; max-height: 126px; width: auto; height: auto; object-fit: contain }
.mlib-gone { font-size: .78rem; font-weight: 600; color: var(--admin-danger) }

.mlib-form { padding: 12px; display: flex; flex-direction: column; gap: 4px }
.mlib-label {
  font-size: .66rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--admin-text-muted); margin-top: 6px;
}
.mlib-form .admin-form-control { padding: 6px 9px; font-size: .8rem }
.mlib-file { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .74rem }

.mlib-meta {
  display: flex; gap: 12px; margin-top: 10px;
  font-size: .7rem; color: var(--admin-text-muted);
}
.mlib-actions { margin-top: 10px }
.mlib-delete { padding: 0 12px 12px; margin-top: auto }

/* The count badge on the active tab was a white veil over the accent, which
   composites to a mid-brown: white on it measured 4.29:1, under the 4.5 that
   11px text needs. Darkening the veil instead of lightening it keeps the badge
   readable as a badge and the number readable as a number. */
.mlib-tab.is-on span { background: rgba(0, 0, 0, .28) }

/* ============================================================================
   ADMIN — what a customer uses
   ========================================================================= */
.use-picker {
  display: grid; gap: 6px; max-height: 240px; overflow-y: auto;
  padding: 10px; border: 1px solid var(--admin-border); border-radius: 10px;
  background: var(--admin-bg, #f8fafc);
}
.use-option { display: flex; align-items: center; gap: 9px; font-size: .84rem; cursor: pointer }
.use-option input { width: 15px; height: 15px; margin: 0; cursor: pointer; flex-shrink: 0 }

/* ============================================================================
   WHO USES IT — customers on a product or service page
   ========================================================================= */
.uses-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center;
}
.uses-item {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; border-radius: 100px;
  border: 1px solid var(--color-border);
  /* Paints its own surface, so it carries its own text colours: these pages
     alternate light and dark and the pill must read on both. */
  background: var(--c-white);
  --color-text:       var(--c-gray-900);
  --color-text-muted: rgba(15, 15, 23, .62);
  color: var(--color-text);
}
.uses-item img { width: auto; height: 26px; max-width: 56px; object-fit: contain; flex-shrink: 0 }
.uses-name { font-size: .92rem; font-weight: 600; color: var(--color-text) }
.uses-place { font-size: .74rem; color: var(--color-text-muted) }

@media (max-width: 767px) {
  .uses-item { padding: 10px 14px }
  .uses-place { display: none }
}

/* ============================================================================
   HERO FACTS — a monospace strip of things that are true

   Numeric, quiet, and set in the mono stack so figures line up and read as
   data rather than as copy. The dividers do the separating, not boxes.
   ========================================================================= */
.hero-facts {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 0; margin: 0 auto var(--space-2xl);
  border-top: 1px solid var(--color-border);
  border-bottom: 1px solid var(--color-border);
  max-width: 640px;
}

.hero-fact {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  flex: 1 1 0; min-width: 110px;
  padding: 14px 18px;
}
.hero-fact + .hero-fact { border-left: 1px solid var(--color-border) }

.hero-fact dt {
  font-size: .64rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--color-text-subtle);
}
.hero-fact dd {
  margin: 0; display: flex; align-items: baseline; gap: 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.hero-fact-value {
  font-size: 1.15rem; font-weight: 600; letter-spacing: -.01em;
  color: var(--color-text); font-variant-numeric: tabular-nums;
}
.hero-fact-note { font-size: .68rem; color: var(--color-text-subtle) }

@media (max-width: 767px) {
  .hero-facts { max-width: 100%; margin-bottom: var(--space-xl) }
  .hero-fact { padding: 11px 10px; min-width: 92px }
  .hero-fact-value { font-size: 1rem }
}

/* ============================================================================
   HERO HUD — cursor, scroll depth, time on page

   Decoration. aria-hidden in the markup, off under reduced motion, and never
   shown to a pointer that cannot hover, because a cursor readout on a
   touchscreen reports a cursor that is not there.
   ========================================================================= */
.hud {
  position: fixed; left: 20px; bottom: 20px; z-index: 40;
  display: grid; gap: 3px;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: 10px;
  background: rgba(8, 8, 12, .55);
  backdrop-filter: blur(8px);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .64rem; line-height: 1.5; letter-spacing: .04em;
  color: rgba(255, 255, 255, .5);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.hud[hidden] { display: none }

.hud-row { display: flex; gap: 8px; justify-content: space-between }
.hud-key { text-transform: uppercase }
.hud-val { color: rgba(255, 255, 255, .82) }

/* It sits over whatever section it happens to be above, so it carries its own
   dark plate rather than inheriting a surface it cannot predict. */
@media (max-width: 1023px) {
  .hud { display: none }
}
@media (prefers-reduced-motion: reduce) {
  .hud { display: none }
}

/* The HUD is fixed, so it floats over light sections as well as dark ones. At
   55% opacity the light section showed through and the plate composited to
   #777779, where the text measured 2.32:1. A translucent surface makes
   contrast depend on whatever happens to be behind it; an opaque one does not.
   The blur went with it -- there is nothing left to see through. */
.hud {
  background: #0c0c14;
  backdrop-filter: none;
  color: rgba(255, 255, 255, .62);
}
.hud-val { color: rgba(255, 255, 255, .95) }

/* The eyebrow joins the facts strip's voice: same mono stack, same tracking.
   It was brand-orange uppercase sans directly above a monospace data strip,
   which is two treatments for the same kind of text. */
.hero-meta {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .68rem; letter-spacing: .18em;
}

/* Last gap in the column: 40 like the three above it, not 48. */
.hero-facts { margin-bottom: 40px }

/* The orbit passes behind the hero text -- by design for the headline, and by
   up to 15px behind the eyebrow at 1280. Both layers were z-index 1, so which
   one won was decided by source order rather than by anything stated. Raising
   the content makes the text reliably the thing on top. */
#hero-content { z-index: 2 }

/* ============================================================================
   ORBIT — the product's own mark

   The tile used to hold a 28px emoji. It holds a logo now, so the image needs
   to sit inside the padding rather than fill the tile, and it must not stretch:
   these are six different marks at six different aspect ratios.
   ========================================================================= */
.orbit-logo {
  width: 68%; height: 68%;
  object-fit: contain;
  display: block;
  /* The tiles carry a brand gradient and several marks are dark on
     transparent, so they get a light plate to sit on -- the same problem the
     product hero logo had, and the same answer. */
  background: #fff;
  border-radius: 10px;
  padding: 5px;
}

/* ============================================================================
   HERO — a narrower column, a stacked CTA, a bigger ring

   The ring radius is derived from the content box, so narrowing the column is
   what gives the orbit room to travel rather than hugging the text.
   ========================================================================= */
#hero-content { max-width: 720px }

.hero-buttons-stacked {
  display: flex; flex-direction: column; align-items: center;
  gap: 12px;
}
.hero-buttons-stacked .btn { width: 100%; max-width: 300px; justify-content: center }

/* ============================================================================
   BALANCED GRIDS

   These all used repeat(auto-fit, minmax(Npx, 1fr)), which asks how many
   columns fit the container and knows nothing about how many items there are.
   Eight industries in a 1000px row came out five and then three, with two
   empty cells hanging off the end.

   The column count now comes from the items themselves, written onto the
   element as --cols by App\Support\BalancedGrid. The fallbacks are what each
   grid used to fit, so a grid rendered without the helper looks unchanged.

   minmax(0, 1fr) rather than 1fr: a grid track's default minimum is auto, so
   one long unbroken word would otherwise push the row wider than its
   container.
   ========================================================================= */
.industry-grid    { grid-template-columns: repeat(var(--cols, 5), minmax(0, 1fr)) }
.cs-metrics       { grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr)) }
.matrix-features  { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) }
.team-card-grid   { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) }
.admin-media-slots{ grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) }

@media (max-width: 900px) {
  .industry-grid    { grid-template-columns: repeat(var(--cols-md, 3), minmax(0, 1fr)) }
  .cs-metrics       { grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
  .matrix-features  { grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
  .team-card-grid   { grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
  .admin-media-slots{ grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
}

@media (max-width: 560px) {
  .industry-grid    { grid-template-columns: repeat(var(--cols-sm, 2), minmax(0, 1fr)) }
  .cs-metrics       { grid-template-columns: repeat(var(--cols-sm, 2), minmax(0, 1fr)) }
  .matrix-features  { grid-template-columns: repeat(var(--cols-sm, 1), minmax(0, 1fr)) }
  .team-card-grid   { grid-template-columns: repeat(var(--cols-sm, 1), minmax(0, 1fr)) }
  .admin-media-slots{ grid-template-columns: repeat(var(--cols-sm, 1), minmax(0, 1fr)) }
}

/* Below this the tiles are narrower than their own labels, which the previous
   rules already handled by dropping to one column. Kept. */
@media (max-width: 380px) {
  .industry-grid, .cs-metrics { grid-template-columns: 1fr }
}

/* The three homepage grids join the balanced set. Fallbacks are the counts
   each was authored with, so a grid rendered without the helper is unchanged. */
.problem-grid { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) }
.help-grid    { grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr)) }
.product-grid { grid-template-columns: repeat(var(--cols, 3), minmax(0, 1fr)) }

@media (max-width: 900px) {
  .problem-grid { grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
  .help-grid    { grid-template-columns: repeat(var(--cols-md, 1), minmax(0, 1fr)) }
  .product-grid { grid-template-columns: repeat(var(--cols-md, 2), minmax(0, 1fr)) }
}

@media (max-width: 560px) {
  .problem-grid, .help-grid, .product-grid { grid-template-columns: 1fr }
}

/* .content-card-grid holds card triplets and appears in ten places with
   identical markup, so it takes a CSS fix rather than the helper -- patching
   ten identical anchors by position is how a wrong one gets edited.

   Three cards in two columns left 2+1 at the tablet width. Three columns hold
   down to 700px, where each card is still around 220px, and below that it is
   one column. Both fill. A grid here with four items would sit 3+1; none
   currently does, and if one appears it wants the helper rather than this. */
@media (max-width: 900px) {
  .content-card-grid { grid-template-columns: repeat(3, minmax(0, 1fr)) }
}
@media (max-width: 700px) {
  .content-card-grid { grid-template-columns: 1fr }
}


/* ── Sidebar collapse ──────────────────────────────────────────────────────
   The sidebar carries six sections and about thirty links, and on a laptop it
   takes 252px of a 1280px screen away from the tables it sits beside. This
   collapses it to an icon rail.

   Desktop only. Below 768px the sidebar is already an off-canvas drawer with
   a hamburger, and a second way to hide the same thing would fight it.

   Labels are clipped rather than display:none, so a link keeps its accessible
   name in the rail and only loses it visually. The tooltip is a convenience
   on top of that, never the only label.
   ────────────────────────────────────────────────────────────────────────── */
.admin-sidebar-logo{position:relative}
.admin-sidebar{transition:width .18s ease}

.admin-sidebar-toggle{
  position:absolute;top:50%;right:12px;transform:translateY(-50%);
  width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  border-radius:8px;cursor:pointer;
  background:var(--admin-sidebar-btn-bg);
  border:1px solid var(--admin-sidebar-btn-border);
  color:var(--admin-sidebar-btn-text);
  transition:background .18s,color .18s,transform .18s;
}
.admin-sidebar-toggle:hover{background:var(--admin-sidebar-btn-hover-bg);color:var(--admin-sidebar-text-bright)}
.admin-sidebar-toggle:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
@media(max-width:767px){.admin-sidebar-toggle{display:none}}

@media(min-width:768px){
  html.admin-sidebar-collapsed .admin-sidebar{width:76px}

  /* The button leaves the corner and centres under the mark: at 76px there is
     no room for it beside the logo. */
  html.admin-sidebar-collapsed .admin-sidebar-toggle{
    position:static;transform:none;margin:14px auto 0;
  }
  html.admin-sidebar-collapsed .admin-sidebar-toggle svg{transform:rotate(180deg)}
  html.admin-sidebar-collapsed .admin-sidebar-logo{padding:22px 12px 18px}
  html.admin-sidebar-collapsed .admin-sidebar-logo-inner{justify-content:center;gap:0}

  html.admin-sidebar-collapsed .admin-sidebar-logo-text,
  html.admin-sidebar-collapsed .admin-sidebar-nav a span,
  html.admin-sidebar-collapsed .admin-sidebar-footer-name,
  html.admin-sidebar-collapsed .admin-sidebar-footer-role,
  html.admin-sidebar-collapsed .admin-sidebar-footer-text{
    position:absolute;width:1px;height:1px;padding:0;margin:0;
    overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);
    white-space:nowrap;border:0;
  }

  html.admin-sidebar-collapsed .admin-sidebar-nav a{
    justify-content:center;gap:0;padding:11px 0;margin:2px 14px;
  }

  /* A section heading with no room for its words becomes the rule it was
     already acting as. */
  html.admin-sidebar-collapsed .admin-sidebar-section{
    height:1px;padding:0;margin:12px 20px;overflow:hidden;
    color:transparent;background:var(--admin-sidebar-hairline);
  }

  html.admin-sidebar-collapsed .admin-sidebar-footer{padding:14px 10px 18px}
  html.admin-sidebar-collapsed .admin-sidebar-logout-btn{
    text-align:center;padding:8px 0;margin-top:0;
  }
}

@media(prefers-reduced-motion:reduce){
  .admin-sidebar,.admin-sidebar-toggle{transition:none}
}

/* ── Solution finder: recommended services ─────────────────────────────────
   These chips set background:var(--ink) -- an alias of --color-surface, a
   light plate -- while their text inherited the surrounding dark section's
   white ink. The service names rendered white on white; only the emoji was
   visible.

   They paint no background now, so they inherit the section's ink and the
   contrast is whatever that section already guarantees. Nothing here assumes
   a light or a dark surround, so the chips are correct in either.
   ────────────────────────────────────────────────────────────────────────── */
.finder-service{
  display:inline-flex;align-items:center;gap:8px;
  padding:10px 18px;border-radius:10px;
  border:1px solid var(--color-border);
  background:transparent;color:inherit;text-decoration:none;
  font-size:.88rem;font-weight:500;line-height:1.3;
  transition:border-color .18s ease,transform .18s ease;
}
.finder-service:hover{border-color:currentColor;transform:translateY(-1px)}
.finder-service:focus-visible{outline:2px solid currentColor;outline-offset:3px}

@media (prefers-reduced-motion: reduce){
  .finder-service{transition:none}
  .finder-service:hover{transform:none}
}

/* ── Customer band, full width ─────────────────────────────────────────────
   The marquee used to sit inside the content column, which made sixteen real
   customers read as a footnote to the claim above them. As its own band it
   spans the viewport and works as the transition into the proof.

   It carries no tone class, so it inherits the page rather than taking a turn
   in the light/dark rhythm. The hairlines are what separate it, not a fill.
   ────────────────────────────────────────────────────────────────────────── */
.cmq{
  margin-top:0;
  padding:var(--space-5) 0;
  border-top:1px solid var(--color-border);
  border-bottom:1px solid var(--color-border);
}
.cmq-label{text-align:center}

/* ── Industry tiles ────────────────────────────────────────────────────────
   A tile says what is behind it now -- how many products and services this
   sector actually has -- so the grid reads as evidence rather than as a list
   of sectors anyone could claim. The counts come from pivot rows, so a tile
   with nothing linked shows nothing at all rather than a zero.
   ────────────────────────────────────────────────────────────────────────── */
.industry-tile{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:2px;padding:26px 18px;text-align:center;
  border-radius:16px;text-decoration:none;color:inherit;
  border:1px solid var(--color-border);
  transition:border-color .2s ease,transform .2s ease;
}
.industry-tile:hover{border-color:currentColor;transform:translateY(-3px)}
.industry-tile:focus-visible{outline:2px solid currentColor;outline-offset:3px}

.industry-tile-mark{
  display:inline-flex;align-items:center;justify-content:center;
  width:52px;height:52px;margin-bottom:10px;border-radius:14px;
  border:1px solid var(--color-border);
}
.industry-meta{
  display:block;margin-top:6px;
  font-size:.76rem;font-weight:400;letter-spacing:.01em;
  color:var(--color-text-dim);
}

/* ── Typed industry keyword ────────────────────────────────────────────────
   The sizer holds the widest sector name at all times and is invisible; the
   visible word sits on top of it. That is what stops the heading reflowing
   every time a letter is added, which is the usual cost of this effect.
   ────────────────────────────────────────────────────────────────────────── */
/* The sizer used to hold the widest name -- "Beauty & Personal Care" --
   at all times, which left a sixteen-character hole after a short word
   like "Retail" and made the sentence read as broken. It now carries the
   current word instead, and the box transitions between widths, so the
   line closes up around short names and opens for long ones without the
   hard jump that reserving nothing would cause. */
.ind-kw{
  position:relative;display:inline-block;vertical-align:baseline;
  transition:width .42s cubic-bezier(.22,1,.36,1);
}
.ind-kw-sizer{visibility:hidden;white-space:nowrap}
.ind-kw-text{position:absolute;left:0;top:0;white-space:nowrap;transform-origin:0 50%}

/* The word drops in tilted, holds, then falls away. Transform and opacity
   only: the effect this came from animated font-size and margin, which
   relayout the heading on every frame -- visible as jitter in a centred
   two-line h2, and expensive on a phone.

   Durations are duplicated in js/industry-rotator.js, which has to know
   when each half is finished in order to swap the text between them. */
.ind-kw-text.is-in{animation:indKwIn .52s cubic-bezier(.22,1,.36,1) both}
.ind-kw-text.is-out{animation:indKwOut .46s cubic-bezier(.55,.06,.68,.19) both}

@keyframes indKwIn{
  0%{opacity:0;transform:translate(-26px,-34px) rotate(-24deg) scale(.45)}
  55%{opacity:1}
  100%{opacity:1;transform:translate(0,0) rotate(0deg) scale(1)}
}
@keyframes indKwOut{
  0%{opacity:1;transform:translate(0,0) rotate(0deg) scale(1)}
  35%{opacity:.55}
  100%{opacity:0;transform:translate(18px,86px) rotate(13deg) scale(.35)}
}

/* Reduced motion keeps the first sector, held. The script returns early
   for the same query, so this only has to undo the transition. */
@media (prefers-reduced-motion: reduce){
  .ind-kw{transition:none}
  .ind-kw-text.is-in,.ind-kw-text.is-out{animation:none}
}

@media (prefers-reduced-motion: reduce){
  .industry-tile{transition:none}
  .industry-tile:hover{transform:none}
}

/* ── /solutions: the comparison and the outcome rows ──────────────────────
   Two blocks that exist only on this page. Both are laid out with the tokens
   the rest of the site uses, so nothing here introduces a second scale.
   ────────────────────────────────────────────────────────────────────────── */

/* On your own / with us. Two columns that stack rather than shrink: a
   comparison read side by side at 320px wide is not a comparison. */
.split-compare{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--space-3);max-width:960px;margin:0 auto;
}
@media(max-width:820px){ .split-compare{grid-template-columns:1fr} }

.plain-list{list-style:none;margin:14px 0 0;padding:0;display:grid;gap:10px}
.plain-list li{
  position:relative;padding-left:22px;
  font-size:.92rem;line-height:1.6;color:var(--color-text-dim);
}
/* A dash for the work you would be doing, a tick for the work that is done
   for you. The two columns should not read as the same list twice. */
.plain-list li::before{
  content:'–';position:absolute;left:0;top:0;opacity:.6;
}
.plain-list--done li{color:var(--color-text)}
.plain-list--done li::before{content:'✓';opacity:1;font-weight:600}

/* One outcome per row: the promise on the left, the services that deliver it
   underneath. The services are the point of the page, so they are listed
   rather than hidden behind the card. */
.outcome-stack{display:grid;gap:var(--space-3);max-width:1000px;margin:0 auto}
.outcome-row{display:block}
.outcome-row-head{display:flex;gap:16px;align-items:flex-start}
.outcome-row-head .card-icon{flex-shrink:0;margin:0}

.outcome-row-title{
  display:inline-flex;align-items:center;gap:8px;
  font-size:1.04rem;font-weight:600;color:inherit;text-decoration:none;
  margin-bottom:6px;
}
.outcome-row-title:hover{text-decoration:underline;text-underline-offset:3px}

.outcome-row-services{
  display:flex;flex-wrap:wrap;gap:8px;
  margin-top:16px;padding-top:16px;
  border-top:1px solid var(--color-border);
}

@media(max-width:560px){
  .outcome-row-head{flex-direction:column;gap:10px}
}

/* Solution headings on /services link through to the solution they name.
   The heading is the grouping, so it should be the way into it. */
.cat-header-link{color:inherit;text-decoration:none}
.cat-header-link:hover{text-decoration:underline;text-underline-offset:4px}
.cat-header-link:focus-visible{outline:2px solid currentColor;outline-offset:3px}

/* ── Neon keyword highlight ──
   Independent of mark.kw (the scroll-triggered highlighter reveal above).
   This is a hover-triggered underline glow for calling out a keyword inline,
   anywhere in copy — not tied to any one section or the hero.
   Brand-mapped: orange is the primary accent, cyan the secondary, in place
   of the reference demo's green/orange pair.
   Motion (color shift + 1px lift) is gated by prefers-reduced-motion below;
   the underline reveal itself is treated as decorative motion too. */
.neon-word{
  color: inherit;
  position: relative;
  transition: color .35s ease, transform .35s ease;
}
.neon-orange{
  --neon-color: var(--color-brand-2, #F98603);
  --neon-underline: linear-gradient(90deg,#c9660a,#F98603,#ffb347,#ffd9a3);
  --neon-glow: rgba(249,134,3,.42);
}
.neon-cyan{
  --neon-color: var(--c-cyan-400, #22d3ee);
  --neon-underline: linear-gradient(90deg,#0e7490,#22d3ee,#67e8f9,#cffafe);
}
/* The second tone the typewriter alternates to. Kept as the supplied
   values rather than remapped to a brand token: this pair is the spec. */
.neon-green{
  --neon-color: #20e86a;
  --neon-underline: linear-gradient(90deg,#087f3e,#00c853,#39ff14,#a7ff83);
  --neon-glow: rgba(32,232,106,.38);
}
.neon-word:hover{
  color: var(--neon-color);
  transform: translateY(-1px);
}
.neon-word::after{
  content: '';
  position: absolute;
  left: 2%; right: 2%; bottom: -0.09em;
  height: 0.055em;
  border-radius: 999px;
  background: var(--neon-underline);
  transform: scaleX(0);
  transform-origin: left;
  opacity: 0;
  transition: transform .4s cubic-bezier(.22,1,.36,1), opacity .25s ease;
}
.neon-word:hover::after{
  transform: scaleX(1);
  opacity: 1;
}
@media (hover:none){
  /* No hover on touch: show the underline at rest instead of hiding the
     effect entirely, since there is no hover event to reveal it. */
  .neon-word::after{ transform: scaleX(1); opacity: .55; }
}
@media (prefers-reduced-motion: reduce){
  .neon-word{ transition: color .01ms; }
  .neon-word:hover{ transform: none; }
  .neon-word::after{ transition: opacity .15s ease; transform: scaleX(1); }
}

/* ── Hero product strip (mobile fallback for the coin ring) ──────────────────
   Same drifting-marquee technique as the customer band (.cmq-*), reusing the
   generic cmq-drift keyframe, but under its own class names. The two markups
   render on the same page -- the hero strip unconditionally (its own
   visibility is toggled by JS via body.orbit-off), the customer band only
   once there are enough customers -- so sharing .cmq-* verbatim made a test
   asserting "how many marquees are on this page" count both as one.
   ────────────────────────────────────────────────────────────────────────── */
.hstrip { margin: 0; }

.hstrip-viewport {
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}

.hstrip-track {
  display: flex; width: max-content;
  animation: cmq-drift calc(var(--hstrip-count, 6) * 4.5s) linear infinite;
  will-change: transform;
}
.hstrip-viewport:hover .hstrip-track,
.hstrip-track:focus-within { animation-play-state: paused }

.hstrip-run {
  display: flex; align-items: center; gap: var(--space-2);
  list-style: none; margin: 0; padding: 0 var(--space-1);
}

/* Self-painting, same reason as .cmq-item: this sits in the hero's dark
   section, and --color-text resolves to white there. A white-on-white pill
   is the exact bug that comment on .cmq-item exists to prevent. */
.hstrip-item {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  height: 56px; padding: 0 20px;
  border: 1px solid rgba(15, 15, 23, .1);
  border-radius: 100px;
  background: var(--c-white);
  color: var(--c-gray-900);
  white-space: nowrap;
}
.hstrip-item img {
  width: auto; height: 28px; max-width: 64px;
  object-fit: contain; flex-shrink: 0; border-radius: 50%;
}
.hstrip-name { font-size: .92rem; font-weight: 600; color: inherit }

@media (max-width: 767px) {
  .hstrip-item { height: 48px; padding: 0 15px; gap: 8px }
  .hstrip-item img { height: 22px; max-width: 48px }
  .hstrip-name { font-size: .84rem }
}

@media (prefers-reduced-motion: reduce) {
  .hstrip-viewport { mask-image: none; overflow: visible }
  .hstrip-track { animation: none; width: 100%; flex-wrap: wrap; justify-content: center }
  .hstrip-run { flex-wrap: wrap; justify-content: center; gap: 10px }
  .hstrip-track .hstrip-run + .hstrip-run { display: none }
}

/* ── Hero product strip (mobile fallback for the coin ring) ──────────────────
   Same drifting-marquee technique as the customer band (.cmq-*), reusing the
   generic cmq-drift keyframe, but under its own class names. The two markups
   render on the same page -- the hero strip unconditionally (its own
   visibility is toggled by JS via body.orbit-off), the customer band only
   once there are enough customers -- so sharing .cmq-* verbatim made a test
   asserting "how many marquees are on this page" count both as one.
   ────────────────────────────────────────────────────────────────────────── */
.hstrip { margin: 0; }

.hstrip-viewport {
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}

.hstrip-track {
  display: flex; width: max-content;
  animation: cmq-drift calc(var(--hstrip-count, 6) * 4.5s) linear infinite;
  will-change: transform;
}
.hstrip-viewport:hover .hstrip-track,
.hstrip-track:focus-within { animation-play-state: paused }

.hstrip-run {
  display: flex; align-items: center; gap: var(--space-2);
  list-style: none; margin: 0; padding: 0 var(--space-1);
}

/* Self-painting, same reason as .cmq-item: this sits in the hero's dark
   section, and --color-text resolves to white there. A white-on-white pill
   is the exact bug that comment on .cmq-item exists to prevent. */
.hstrip-item {
  display: flex; align-items: center; gap: 10px; flex-shrink: 0;
  height: 56px; padding: 0 20px;
  border: 1px solid rgba(15, 15, 23, .1);
  border-radius: 100px;
  background: var(--c-white);
  color: var(--c-gray-900);
  white-space: nowrap;
}
.hstrip-item img {
  width: auto; height: 28px; max-width: 64px;
  object-fit: contain; flex-shrink: 0; border-radius: 50%;
}
.hstrip-name { font-size: .92rem; font-weight: 600; color: inherit }

@media (max-width: 767px) {
  .hstrip-item { height: 48px; padding: 0 15px; gap: 8px }
  .hstrip-item img { height: 22px; max-width: 48px }
  .hstrip-name { font-size: .84rem }
}

@media (prefers-reduced-motion: reduce) {
  .hstrip-viewport { mask-image: none; overflow: visible }
  .hstrip-track { animation: none; width: 100%; flex-wrap: wrap; justify-content: center }
  .hstrip-run { flex-wrap: wrap; justify-content: center; gap: 10px }
  .hstrip-track .hstrip-run + .hstrip-run { display: none }
}

/* ── Neon typewriter ──────────────────────────────────────────────────────
   One tagged phrase, one tone, filled with that tone's gradient the way
   .grad-text fills text. Colour alternates between phrases down the page,
   never within one.

   Width drives the reveal, so the rest of the line moves along as the phrase
   types rather than sitting in reserved space.
   ────────────────────────────────────────────────────────────────────────── */
.nt {
  /* The tone's gradient, painted through the glyphs. --neon-underline is
     already a 90deg four-stop, which is what the fluid-gradient rules ask
     for, so it doubles as the fill. */
  /* Reserves the underline's own space rather than hanging it below the
     box. At bottom:-0.09em it sat in the next line's territory, which is
     invisible today and a collision the first time one of these wraps. */
  padding-bottom: .09em;
  background: var(--neon-underline);
  background-size: var(--gf-stretch, 260% 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  animation: gradFlow var(--gf-dur-text, 6s) var(--gf-ease, linear) infinite;
}

/* Clipped while it types. inline-block is what makes width animatable at
   all; vertical-align:bottom is the counterweight, because an inline-block
   with overflow:hidden takes its baseline from the bottom margin edge rather
   than the text baseline and would otherwise sit low on the line. */
.nt-armed {
  display: inline-block;
  vertical-align: bottom;
  overflow: hidden;
  white-space: nowrap;
}

/* The caret. Present only while typing, so the phrase is not left with a
   border once it is whole. Its colour is the tone's, not the gradient's --
   a border cannot be painted by background-clip. */
.nt-typing {
  border-right: 2px solid var(--neon-color);
  animation: gradFlow var(--gf-dur-text, 6s) var(--gf-ease, linear) infinite,
             nt-blink .5s step-end infinite alternate;
}

@keyframes nt-blink {
  50% { border-color: transparent; }
}

/* Hover underline, once the phrase is whole. Gated on nt-done so it cannot
   glow beneath a half-typed phrase. */
.nt-done { position: relative; }
.nt-done::after {
  content: '';
  position: absolute;
  /* Edge to edge. The old 2% inset stopped the line short of the phrase
     it belongs to, which reads as a mistake rather than a margin. */
  left: 0; right: 0; bottom: 0;
  height: 0.055em;
  /* 0.055em rounds below a pixel at body sizes and the line vanishes. */
  min-height: 3px;
  border-radius: 999px;
  background: var(--neon-underline);
  /* Neon is the bloom, not the bar. Without this it is a coloured rule. */
  box-shadow: 0 0 7px var(--neon-glow), 0 0 14px var(--neon-glow);
  transform: scaleX(0);
  transform-origin: left center;
  opacity: 0;
  transition: transform .44s cubic-bezier(.22,1,.36,1), opacity .22s ease;
}
.nt-done:hover::after { transform: scaleX(1); opacity: 1; }

@media (hover: none) {
  .nt-done::after { transform: scaleX(1); opacity: .5; }
}

@media (prefers-reduced-motion: reduce) {
  /* The gradient fill stays; its drift does not. */
  .nt { animation: none; }
  .nt-done::after { transition: opacity .15s ease; }
}

/* ── Living backdrop: one shared field behind the dark chapters ───────────
   Fixed, full viewport, sitting under the whole page. Dark sections are
   translucent so it reads through them; light sections are opaque so it does
   not, and the page's light chapters stay exactly as designed.

   No z-index anywhere here. body carries an opaque background, so a negative
   z-index would hide this behind it; the backdrop is body's first child and
   every section is position:relative later in the DOM, which paints them
   above it without either needing a layer.

   Two elements per blob: the wrapper is GSAP's (parallax) and the inner
   circle is the keyframes' (drift). One element for both would collide -- a
   CSS animation beats an inline style on the property it animates, so the
   drift would erase the parallax every frame.
   ────────────────────────────────────────────────────────────────────────── */
.page-backdrop {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(165deg,#08080a 0%,#0d0906 55%,#060608 100%);
}

.page-backdrop .lblob-wrap {
  position: absolute;
  will-change: transform;
}

.page-backdrop .lblob {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  will-change: transform;
  animation: blob-orbit linear infinite;
}

@keyframes blob-orbit {
  to { transform: translate3d(0, 0, 1px) rotate(360deg); }
}

/* Two white, one orange. White carries the light on a near-black field; the
   orange is what stops it reading as grey haze. */
.page-backdrop .lblob-wrap-1 { width: 34vmin; height: 34vmin; top: 8%;  left: 6%;  }
.page-backdrop .lblob-wrap-2 { width: 28vmin; height: 28vmin; top: 58%; left: 74%; }
.page-backdrop .lblob-wrap-3 { width: 40vmin; height: 40vmin; top: 34%; left: 40%; }

.page-backdrop .lblob-1 {
  /* These are read through a section at 28 per cent, so the number here is
     not what lands on screen: .40 arrives as .112, which is the visible
     figure. Set for direct viewing, as they were, they vanished. */
  background: #ffffff; opacity: .40; filter: blur(70px);
  transform-origin: 18vw 6vh;   animation-duration: 43s; animation-delay: -7s;
}
.page-backdrop .lblob-2 {
  background: #ffffff; opacity: .34; filter: blur(80px);
  transform-origin: -16vw -9vh; animation-duration: 57s; animation-delay: -23s;
}
.page-backdrop .lblob-3 {
  background: var(--blob-color-1, #F98603); opacity: .48; filter: blur(90px);
  transform-origin: -21vw 12vh; animation-duration: 71s; animation-delay: -38s;
}

/* Blurred animated blobs are the most expensive thing painted here. One is
   enough to keep a phone feeling alive; three is where it costs frames. */
@media (max-width: 767px) {
  .page-backdrop .lblob-wrap-2,
  .page-backdrop .lblob-wrap-3 { display: none; }
  .page-backdrop .lblob-1 { filter: blur(52px); }
}

@media (prefers-reduced-motion: reduce) {
  .page-backdrop .lblob { animation: none; }
}

/* ── Neon letters ─────────────────────────────────────────────────────────
   The design's payoff, paired with the gradient fill rather than replacing
   it.

   At rest the letters are transparent and the phrase's gradient shows
   through them -- nothing about the resting state changes. On hover they
   take a solid fill of their own, so the gradient gives way to lit type.
   That is the design's monochrome-to-neon transition, with our resting
   state standing in for its monochrome.

   Two granularities, as the design has: hovering a word lights all of its
   letters, hovering a single letter singles that one out further.

   position:relative + top rather than inline-block + transform. Transforms
   do not apply to inline elements, so the lift would have forced
   inline-block on every character -- which kills kerning between adjacent
   letters (visible immediately in a serif heading), lets a line break fall
   between any two characters, and changes the rendered width the typewriter
   measures before clipping. top on a relatively-positioned inline costs a
   layout pass on hover, which for fifteen characters is nothing next to
   losing the kerning of every headline on the page.

   NOT taken from the design: per-letter colour. It cycles green, cyan, pink
   and orange across the characters. A phrase here is one tone -- the rule
   already set for this text -- so letters light in their phrase's own tone
   and the fragmentation lives in the interaction, not the palette.
   ────────────────────────────────────────────────────────────────────────── */

.nt-l {
  position: relative;
  transition: top .36s cubic-bezier(.22,1,.36,1),
              text-shadow .30s ease,
              -webkit-text-fill-color .26s ease,
              color .26s ease;
}

/* The solid fill overrides the transparent one inherited from .nt, so the
   gradient stops showing through this glyph and the letter reads as itself.
   The glow is the tone's, not a hardcoded rgba. */
.nt-done .nt-w:hover .nt-l,
.nt-done .nt-l:hover {
  -webkit-text-fill-color: var(--neon-color);
  color: var(--neon-color);
  top: -1px;
  text-shadow: 0 0 1px var(--neon-color),
               0 0 6px var(--neon-glow),
               0 0 12px var(--neon-glow);
}

/* A character under the pointer outranks its word, so pointing at one still
   singles it out while the rest of the word is lit. */
.nt-done .nt-w:hover .nt-l:hover { top: -2px; }

@media (hover: none) {
  /* No hover to give, so nothing should sit permanently lit. */
  .nt-l { transition: none; }
}

@media (prefers-reduced-motion: reduce) {
  .nt-l { transition: none; }
  .nt-done .nt-w:hover .nt-l,
  .nt-done .nt-l:hover,
  .nt-done .nt-w:hover .nt-l:hover { top: 0; }
}

/* ── Service pricing tiers ────────────────────────────────────────────────
   Only ever rendered when a service has rows in service_tiers. Laid out on
   the same balanced-grid helper the rest of the site uses, so three tiers
   sit in three columns and four do not leave a widow.
   ────────────────────────────────────────────────────────────────────────── */
.tier-grid{
  display:grid;
  grid-template-columns:repeat(var(--cols,3),minmax(0,1fr));
  gap:var(--space-3);
  max-width:1040px;margin:0 auto;
}
@media(max-width:900px){ .tier-grid{grid-template-columns:repeat(var(--cols-md,2),minmax(0,1fr))} }
@media(max-width:620px){ .tier-grid{grid-template-columns:1fr} }

.tier-card{position:relative;display:flex;flex-direction:column}

/* The highlighted tier is marked by a border and a flag, not by being bigger:
   a scaled card shifts the row and makes the others look like an afterthought. */
.tier-card--featured{border-color:var(--color-brand)}

.tier-flag{
  position:absolute;top:-11px;left:50%;transform:translateX(-50%);
  padding:3px 12px;border-radius:999px;white-space:nowrap;
  font-size:.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  background:var(--color-brand);color:#111827;
}

.tier-price{margin:18px 0 4px;display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.tier-amount{font-size:1.9rem;font-weight:700;letter-spacing:-.02em;line-height:1}
.tier-alt{font-size:1rem;font-weight:500;opacity:.72}
.tier-note{flex-basis:100%;font-size:.78rem;opacity:.66}

.tier-commitment{
  margin:10px 0 0;font-size:.8rem;font-weight:600;
  padding:7px 11px;border-radius:8px;
  background:var(--color-brand-tint);color:var(--color-brand-on-tint);
}

.tier-list{margin-top:16px}
/* Pushes the button to the bottom so buttons line up across tiers of
   different lengths. */
.tier-foot{margin-top:auto;padding-top:20px}

/* ── Tier editor: fields beside a live card ───────────────────────────────
   Mobile first. One column is the default and needs no query; the second
   column only appears once there is genuinely room for a 320px card to sit
   beside the fields without squeezing them.

   Every colour here is an --admin-* token, so the dark set the theme emits
   under prefers-color-scheme applies without this file knowing about it.
   ────────────────────────────────────────────────────────────────────────── */
.tier-editor{ display:grid; gap:var(--space-4, 24px); align-items:start; }

@media (min-width: 1080px){
  .tier-editor{ grid-template-columns:minmax(0,1fr) 340px; }
  /* Follows you down a long form rather than scrolling away at the top. */
  .tier-editor-preview{ position:sticky; top:24px; }
}

.tier-preview-inner{
  border:1px solid var(--admin-border);
  background:var(--admin-card-bg);
  border-radius:14px;
  padding:16px;
}

.tier-count{
  display:inline-block; margin-left:8px; padding:1px 8px; border-radius:999px;
  background:var(--admin-badge-gray-bg); color:var(--admin-badge-gray-text);
  border:1px solid var(--admin-badge-gray-border);
  font-size:.72rem; font-weight:600;
}

/* ── the card itself ── */
.tierp{
  position:relative; display:flex; flex-direction:column;
  border:1px solid var(--admin-border);
  background:var(--admin-bg);
  border-radius:14px; padding:20px;
  transition:border-color .2s ease;
}
.tierp--featured{ border-color:var(--admin-accent); }

.tierp-flag{
  position:absolute; top:-10px; left:50%; transform:translateX(-50%);
  padding:2px 11px; border-radius:999px; white-space:nowrap;
  background:var(--admin-accent); color:var(--admin-text-on-accent);
  font-size:.64rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
}

.tierp-name{ font-weight:700; font-size:1rem; color:var(--admin-text); }
.tierp-summary{ margin:6px 0 0; font-size:.82rem; line-height:1.55; color:var(--admin-text-muted); }

.tierp-price{ margin:14px 0 0; display:flex; flex-wrap:wrap; align-items:baseline; gap:7px; }
.tierp-amount{ font-size:1.55rem; font-weight:700; letter-spacing:-.02em; line-height:1; color:var(--admin-text); }
.tierp-alt{ font-size:.9rem; font-weight:500; color:var(--admin-text-muted); }
.tierp-note{ flex-basis:100%; font-size:.74rem; color:var(--admin-text-muted); }

.tierp-commitment{
  margin:11px 0 0; padding:6px 10px; border-radius:8px;
  background:var(--admin-badge-gray-bg); color:var(--admin-text);
  border:1px solid var(--admin-badge-gray-border);
  font-size:.76rem; font-weight:600;
}

.tierp-list{ margin:14px 0 0; padding:0; list-style:none; display:grid; gap:7px; }
.tierp-list li{
  position:relative; padding-left:19px;
  font-size:.8rem; line-height:1.5; color:var(--admin-text-muted);
}
.tierp-list li::before{
  content:'✓'; position:absolute; left:0; top:0;
  color:var(--admin-accent-text); font-weight:700;
}

.tierp-foot{ margin-top:18px; }
.tierp-btn{
  display:block; text-align:center;
  padding:9px 14px; border-radius:9px;
  border:1px solid var(--admin-border);
  background:var(--admin-btn-secondary-bg); color:var(--admin-text);
  font-size:.8rem; font-weight:600;
}
.tierp--featured .tierp-btn{
  background:var(--admin-accent); color:var(--admin-text-on-accent);
  border-color:var(--admin-accent);
}

@media (prefers-reduced-motion: reduce){
  .tierp{ transition:none; }
}

/* Marks a product the reader can open right now. Sits inside .card-title,
   which is already inside the card's anchor, so it must not be a link. */
.card-flag{display:inline-block;margin-left:8px;padding:2px 8px;border-radius:20px;
  font-family:var(--font-mono,ui-monospace,monospace);font-size:10px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;vertical-align:middle;
  color:var(--color-accent,#c2681f);background:color-mix(in srgb,var(--color-accent,#c2681f) 14%,transparent);
  border:1px solid color-mix(in srgb,var(--color-accent,#c2681f) 34%,transparent)}
.dark-section .card-flag{color:#e89a52;background:rgba(232,154,82,.14);border-color:rgba(232,154,82,.34)}

/* Inline enquiry form. Two columns on desktop -- the reason to write on the
   left, the writing on the right -- and stacked below 900px. */
.enquiry-block{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);
  gap:clamp(28px,5vw,64px);align-items:start}
.enquiry-copy .section-lead{margin-top:10px;max-width:44ch}
.enquiry-form{display:flex;flex-direction:column;gap:18px}
.enquiry-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.enquiry-submit{justify-content:center;padding:15px 22px}
@media(max-width:900px){
  .enquiry-block{grid-template-columns:1fr;gap:26px}
  .enquiry-row{grid-template-columns:1fr}
}

/* Growth check page. */
.gc-hero{max-width:44ch}
.gc-lead{margin:18px 0 30px}
.gc-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:22px}
.gc-card{background:var(--color-surface,#fff);border:1px solid var(--color-border);
  border-radius:14px;padding:26px}
.gc-num{font-family:var(--font-mono,ui-monospace,monospace);font-size:12px;font-weight:600;
  letter-spacing:.1em;color:var(--color-accent,#c2681f);margin-bottom:12px}
.gc-split{display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,.9fr);
  gap:clamp(28px,5vw,60px);align-items:start}
.gc-panel{background:var(--color-surface,#fff);border:1px solid var(--color-border);
  border-radius:14px;padding:8px 24px}
.gc-line{display:flex;justify-content:space-between;gap:18px;padding:15px 0;
  border-bottom:1px solid var(--color-border)}
.gc-line:last-child{border-bottom:none}
.gc-k{font-size:13.5px;color:var(--color-text-dim,#666);white-space:nowrap}
.gc-v{font-size:13.5px;font-weight:600;text-align:right}
.gc-note{max-width:62ch}
.dark-section .gc-card,.dark-section .gc-panel{background:rgba(255,255,255,.04);
  border-color:rgba(255,255,255,.12)}
@media(max-width:820px){.gc-split{grid-template-columns:1fr;gap:26px}}

/* Navigation, not a call to action. Reads as a link because that is what it
   is: styling it as a button made 17 homepage buttons out of 2 real asks. */
.btn-link{background:none;border:none;padding:0;min-height:0;box-shadow:none;
  color:var(--color-accent,#c2681f);font-weight:600;font-size:14.5px;
  text-decoration:none;gap:7px}
.btn-link:hover{background:none;box-shadow:none;text-decoration:underline;
  text-underline-offset:4px}
.btn-link:focus-visible{outline:2px solid var(--color-accent,#c2681f);
  outline-offset:4px;border-radius:3px}
.dark-section .btn-link{color:#e89a52}
.btn-link svg{transition:transform .18s ease}
.btn-link:hover svg{transform:translateX(3px)}
@media(prefers-reduced-motion:reduce){.btn-link svg{transition:none}
  .btn-link:hover svg{transform:none}}

/* Solution finder: shown when nothing we build answers the question. */
.finder-none{max-width:60ch}
.finder-none .body{margin-top:12px}

/* admin sidebar toggle: outside the drawer */
/* Fixed to the drawer's outer edge and straddling it, so the control stays
   in one place whether the drawer is open or collapsed. Previously it lived
   inside the drawer and moved below the logo when collapsed -- the control
   for the rail was inside the rail. */
.admin-sidebar-toggle{
  position:fixed;top:26px;left:252px;transform:translateX(-50%);
  z-index:30;width:26px;height:26px;
  display:flex;align-items:center;justify-content:center;
  border-radius:50%;cursor:pointer;padding:0;
  background:var(--admin-card-bg);
  border:1px solid var(--admin-border);
  color:var(--admin-text-muted);
  box-shadow:0 2px 6px rgba(0,0,0,.22);
  transition:left .2s ease,background .18s,color .18s,border-color .18s;
}
.admin-sidebar-toggle:hover{
  background:var(--admin-accent);border-color:var(--admin-accent);color:#fff;
}
.admin-sidebar-toggle:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
html.admin-sidebar-collapsed .admin-sidebar-toggle{
  position:fixed;top:26px;left:76px;transform:translateX(-50%);margin:0;
}
html.admin-sidebar-collapsed .admin-sidebar-toggle svg{transform:rotate(180deg)}
@media(max-width:767px){.admin-sidebar-toggle{display:none}}

/* Sign Out reads as navigation, because that is what it is. Same shape as
   every other item in the drawer rather than a lone piece of text. */
.admin-sidebar-logout-btn{
  display:flex;align-items:center;gap:10px;
  width:100%;padding:10px 20px;margin:1px 0 0;
  border-radius:10px;font-size:.875rem;font-weight:450;font-family:inherit;
  text-align:left;cursor:pointer;
  background:transparent;border:1px solid transparent;
  color:var(--admin-sidebar-text);
  transition:background .18s,color .18s;
}
.admin-sidebar-logout-btn:hover{background:var(--admin-sidebar-hover-bg);color:#fff}
.admin-sidebar-logout-btn:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
html.admin-sidebar-collapsed .admin-sidebar-logout-btn{
  justify-content:center;gap:0;padding:11px 0;text-align:center;
}
html.admin-sidebar-collapsed .admin-sidebar-logout-btn span{
  width:0;overflow:hidden;opacity:0;white-space:nowrap;
}
@media(prefers-reduced-motion:reduce){
  .admin-sidebar-toggle{transition:none}
}

/* admin heading tooltip */
/* The explanation now hangs off the heading instead of sitting above every
   table. Hover, focus and tap all reveal it; it is a button so the keyboard
   reaches it, and the text is real DOM rather than a title attribute so it
   can carry markup and be announced. */
.admin-help-wrap{position:relative;display:inline-flex;vertical-align:middle;margin-left:7px}
.admin-help-tip{
  display:inline-flex;align-items:center;justify-content:center;
  width:19px;height:19px;padding:0;border-radius:50%;cursor:help;
  background:transparent;border:1px solid var(--admin-border);
  color:var(--admin-text-muted);transition:color .16s,border-color .16s,background .16s;
}
.admin-help-tip:hover,.admin-help-tip:focus-visible{
  color:var(--admin-accent-text,var(--admin-accent));
  border-color:var(--admin-accent);background:transparent;
}
.admin-help-tip:focus-visible{outline:2px solid var(--admin-accent);outline-offset:2px}
.admin-help-bubble{
  position:absolute;top:calc(100% + 9px);left:50%;transform:translateX(-50%) translateY(-3px);
  z-index:40;width:max-content;max-width:340px;
  padding:11px 14px;border-radius:9px;
  background:var(--admin-card-bg);border:1px solid var(--admin-border);
  color:var(--admin-text);
  /* 0.85rem rather than the 0.78rem the paragraph used: this is prose, and it
     is only on screen while someone is reading it. */
  font-size:.85rem;line-height:1.55;font-weight:400;text-align:left;
  white-space:normal;letter-spacing:0;text-transform:none;
  box-shadow:0 8px 24px rgba(0,0,0,.34);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .16s ease,transform .16s ease,visibility .16s;
}
.admin-help-bubble strong{color:var(--admin-text);font-weight:600}
.admin-help-wrap:hover .admin-help-bubble,
.admin-help-tip:focus-visible + .admin-help-bubble{
  opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);
}
/* Near the left edge the bubble would otherwise run off screen. */
.admin-page-header .admin-help-bubble{left:0;transform:translateX(0) translateY(-3px)}
.admin-page-header .admin-help-wrap:hover .admin-help-bubble,
.admin-page-header .admin-help-tip:focus-visible + .admin-help-bubble{
  transform:translateX(0) translateY(0);
}
@media(max-width:560px){
  .admin-help-bubble{max-width:min(78vw,340px)}
}
@media(prefers-reduced-motion:reduce){
  .admin-help-bubble{transition:none}
}

/* admin readability */
/* Helper and muted text sat at .78rem in a dim grey on a dark ground. Small
   and low contrast together is what makes text tiring, so both are lifted --
   the size to something readable and the colour off the background. */
.admin-form-help{font-size:.82rem;line-height:1.6;color:var(--admin-text-muted)}
.admin-table td{line-height:1.55}
.admin-sidebar-footer-role{font-size:.74rem}

/* readability: muted text on dark ground */
/* .body and friends read --color-text-muted rather than inheriting, and that
   token is #6c6a78 -- fine on white, about 3.7:1 on the dark ground this site
   actually uses. Dark sections now carry their own muted family in white
   alphas. Light sections keep the original values. */
.dark-section{
  --color-text:        #fff;
  --color-text-dim:    rgba(255,255,255,.78);
  --color-text-muted:  rgba(255,255,255,.72);
  --color-text-subtle: rgba(255,255,255,.62);
  --color-text-faint:  rgba(255,255,255,.50);
}
/* Body copy should not be the most muted text on the page. Weight 350 is a
   display weight; at this size on a dark ground it reads as grey rather than
   as text. */
.body{font-weight:400}
.product-card-desc{font-weight:400;color:var(--color-text-dim)}
.dark-section .body,
.dark-section p.body{color:var(--color-text-muted)}

/* Small print that was genuinely too small to read rather than deliberately
   set as a label. Labels and badges keep their size; these are sentences. */
.card-more{font-size:.88rem}
.nav-mini-sub{font-size:.78rem;line-height:1.45}
.foot-blurb{font-size:.86rem;line-height:1.65}
.contact-info-note{font-size:.8rem;line-height:1.5}

/* tooltip: align to the heading, not the icon */
/* Centring the bubble on its icon is right for an icon mid-sentence and wrong
   for one hanging off a left-aligned heading: on a short heading the icon sits
   ~70px in, and a 340px bubble centred there runs off the panel. Every use of
   this component is a heading, so left is the sensible default. */
.admin-help-bubble{left:0;transform:translateX(0) translateY(-3px)}
.admin-help-wrap:hover .admin-help-bubble,
.admin-help-tip:focus-visible + .admin-help-bubble{
  transform:translateX(0) translateY(0);
}
/* Near the right edge, flip so it opens leftwards instead. */
.admin-form-group .admin-help-bubble,
.admin-fieldset .admin-help-bubble{left:auto;right:0}

/* dashboard: a funnel shaped like one */
/* Left-aligned stages read as a bar chart and the shape said nothing. Centred,
   the narrowing carries the meaning on its own: wide where everyone arrives,
   narrow where the few become customers. */
.dash-funnel{align-items:center}
.dash-funnel-stage{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:1px;line-height:1.2;
}
.dash-funnel-stage span{font-size:.72rem;font-weight:500;opacity:.92;letter-spacing:.01em}

/* dashboard: cards that say something */
/* Six across gave each card no room to carry anything but a number. Two rows
   of three leaves space for a comparison, which is what makes a figure
   actionable. */
.dash-metrics{grid-template-columns:repeat(3,1fr);gap:16px}
@media(max-width:1100px){.dash-metrics{grid-template-columns:repeat(2,1fr)}}
@media(max-width:680px){.dash-metrics{grid-template-columns:1fr}}

.dash-metric{padding:18px 20px}
.dash-metric-top{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.dash-metric-icon{
  display:inline-flex;align-items:center;justify-content:center;
  width:28px;height:28px;border-radius:8px;flex-shrink:0;
  background:var(--admin-surface-active,rgba(255,255,255,.06));
  color:var(--admin-accent);
}
.dash-metric-label{font-size:.82rem;font-weight:600;color:var(--admin-text)}
.dash-metric-unit{
  margin-left:auto;font-size:.68rem;letter-spacing:.05em;text-transform:uppercase;
  color:var(--admin-text-muted);white-space:nowrap;
}
.dash-metric-value{font-size:2rem;font-weight:700;line-height:1.05;letter-spacing:-.02em;
  font-variant-numeric:tabular-nums;color:var(--admin-text)}
.dash-metric-delta{
  display:inline-flex;align-items:center;gap:5px;margin-top:7px;
  font-size:.76rem;font-weight:600;
}
.dash-metric-arrow{font-size:.9em;line-height:1}
.dash-metric-delta.tone-good{color:var(--admin-success,#3fae6a)}
.dash-metric-delta.tone-bad{color:var(--admin-danger,#d9534f)}
.dash-metric-delta.tone-neutral{color:var(--admin-text-muted)}
.dash-metric-sub{margin-top:8px;font-size:.76rem;line-height:1.5;color:var(--admin-text-muted)}

/* The figure the owner opens the page for. */
.dash-metric.is-primary{border-color:var(--admin-accent)}
.dash-metric.is-primary .dash-metric-value{font-size:2.4rem;color:var(--admin-accent)}
/* A zero that means "nobody recorded anything" should not shout like a result. */
.dash-metric.is-muted .dash-metric-value{color:var(--admin-text-muted)}

/* card rows centre when they do not fill the row */
/* repeat(3, 1fr) is right for three cards and wrong for two: the third column
   stays empty and the row hangs left with a hole beside it. 1fr leaves no
   free space for justify-content to distribute, so the tracks need an upper
   bound before centring can do anything. */
.svc-grid,
.product-grid,
.reason-grid,
.content-card-grid,
.gc-grid,
.tier-grid,
.team-card-grid,
.industry-grid{
  grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),360px));
  justify-content:center;
}
/* Two-up and one-up breakpoints keep the same behaviour rather than falling
   back to stretched tracks. */
@media(max-width:900px){
  .svc-grid,.product-grid,.reason-grid,.content-card-grid,
  .gc-grid,.tier-grid,.team-card-grid,.industry-grid{
    grid-template-columns:repeat(auto-fit,minmax(min(100%,240px),380px));
  }
}
@media(max-width:560px){
  .svc-grid,.product-grid,.reason-grid,.content-card-grid,
  .gc-grid,.tier-grid,.team-card-grid,.industry-grid{
    grid-template-columns:minmax(0,1fr);
  }
}
/* The industry tiles are small by design and read as a set, so they keep a
   tighter track than the content cards. */
.industry-grid{grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),220px))}
@media(max-width:560px){.industry-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* funnel: the chart carries the numbers */
/* Each bar states its own value, label and share, so the legend beneath -- the
   same four figures in words -- was removed. The gap between bars now carries
   the drop-off, which is the number a funnel exists to show and which was not
   on the panel at all. */
.dash-funnel{gap:0}
.dash-funnel-stage{
  padding:13px 14px;border-radius:10px;
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:10px;
  text-align:left;
}
.dash-funnel-value{font-size:1.15rem;font-weight:700;font-variant-numeric:tabular-nums;line-height:1}
.dash-funnel-name{font-size:.82rem;font-weight:600;opacity:.95}
.dash-funnel-pct{
  font-size:.72rem;font-weight:700;font-variant-numeric:tabular-nums;
  padding:2px 7px;border-radius:20px;background:rgba(0,0,0,.16);
}

/* The connector inherits the colour of the stage below it, so a stage can be
   followed down the chart without reading a label. */
.dash-funnel-drop{
  display:flex;flex-direction:column;align-items:center;gap:3px;
  padding:5px 0;
}
.dash-funnel-drop-line{
  width:2px;height:12px;border-radius:2px;
  background:var(--stage,var(--admin-border));opacity:.55;
}
.dash-funnel-drop-text{
  font-size:.7rem;font-weight:600;letter-spacing:.02em;
  color:var(--admin-text-muted);font-variant-numeric:tabular-nums;
}
@media(max-width:520px){
  .dash-funnel-stage{grid-template-columns:auto 1fr;row-gap:2px}
  .dash-funnel-pct{grid-column:2;justify-self:start}
}

/* problem cards: pain, then the turn, then relief */

/* Equal heights with the link pinned to the foot, so a row of cards ends on a
   straight line instead of three different ones. */
.problem-card{height:100%;gap:0}
.problem-card .card-more{margin-top:auto;padding-top:14px}

/* The problem is context. The reader already has the pain; this only has to
   confirm they are looking at the right card, so it steps back. */
.problem-text{
  font-size:.845rem;line-height:1.62;margin:10px 0 0;
  color:var(--color-text-subtle);font-weight:400;
}

/* The outcome is the reason to click, so it is the visual centre. A panel
   rather than a paragraph: the eye lands on the shape before it reads a word. */
.outcome-block{
  position:relative;margin-top:20px;padding:13px 15px 14px;
  border-radius:11px;
  background:color-mix(in srgb,var(--color-brand,#F98603) 8%,transparent);
  border:1px solid color-mix(in srgb,var(--color-brand,#F98603) 20%,transparent);
  transition:background .22s ease,border-color .22s ease,transform .22s ease;
}
/* The connector. A short rule rising out of the panel so the eye travels down
   through the turn rather than reading two separate blocks. */
.outcome-block::before{
  content:'';position:absolute;left:22px;top:-13px;
  width:2px;height:13px;border-radius:2px;
  background:color-mix(in srgb,var(--color-brand,#F98603) 45%,transparent);
}
.outcome-tag{
  display:block;margin:0 0 5px;
  font-size:10.5px;font-weight:700;letter-spacing:1.1px;text-transform:uppercase;
  color:var(--color-brand,#F98603);
  padding:0;border-radius:0;background:none;
}
.outcome-text{
  font-size:.9rem;line-height:1.58;margin:0;
  color:var(--color-text);font-weight:450;
}

/* Dark sections: the tint has to come off the light rather than the dark. */
.dark-section .outcome-block{
  background:rgba(249,134,3,.10);
  border-color:rgba(249,134,3,.24);
}
.dark-section .problem-text{color:var(--color-text-subtle)}

/* One coordinated hover, not three competing ones: the card lifts, the panel
   warms, the arrow moves. */
.problem-card:hover .outcome-block{
  background:color-mix(in srgb,var(--color-brand,#F98603) 13%,transparent);
  border-color:color-mix(in srgb,var(--color-brand,#F98603) 34%,transparent);
  transform:translateY(-1px);
}
.dark-section .problem-card:hover .outcome-block{
  background:rgba(249,134,3,.16);border-color:rgba(249,134,3,.38);
}
.problem-card .card-more svg{transition:transform .22s ease}
.problem-card:hover .card-more svg{transform:translateX(4px)}

/* The old inline pill is gone from the markup; this keeps any stray one from
   rendering at 9.5px, which was below reading size anyway. */
.problem-tag{display:none}

@media(prefers-reduced-motion:reduce){
  .outcome-block,
  .problem-card .card-more svg{transition:none}
  .problem-card:hover .outcome-block{transform:none}
  .problem-card:hover .card-more svg{transform:none}
}

/* problem cards: one line across the row */
/* The panels started at different heights because the problem text above them
   runs from two lines to five. Uniform grey hid that; a tinted block does not.
   Subgrid lets each card take its rows from the grid rather than laying itself
   out alone, so icon, title, problem, panel and link line up across the row
   however long the copy is. */
@supports (grid-template-rows: subgrid){
  .problem-grid{grid-auto-rows:auto}
  .problem-grid > .problem-card{
    display:grid;
    grid-template-rows:subgrid;
    /* icon · title · problem · panel · link */
    grid-row:span 5;
    gap:0;
  }
  .problem-card > .card-icon{grid-row:1;align-self:start}
  .problem-card > .card-title{grid-row:2;align-self:start}
  .problem-card > .problem-text{grid-row:3;align-self:start}
  .problem-card > .outcome-block{grid-row:4;align-self:start}
  .problem-card > .card-more{grid-row:5;align-self:end;margin-top:0}

  /* The rows carry the rhythm now, so the spacing moves off the elements and
     onto the tracks -- otherwise margins fight the alignment. */
  .problem-grid{row-gap:0}
  .problem-card > .card-title{padding-top:14px}
  .problem-card > .problem-text{padding-top:10px;margin:0}
  .problem-card > .outcome-block{margin-top:20px}
  .problem-card > .card-more{padding-top:16px}
}

/* A card with no outcome -- the /solutions listing -- spans one row fewer so
   it does not reserve an empty track for a panel it never renders. */
@supports (grid-template-rows: subgrid){
  .problem-grid > .problem-card:not(:has(.outcome-block)){grid-row:span 5}
  .problem-card:not(:has(.outcome-block)) > .card-more{grid-row:5}
}


/* ── Centred trailing row ─────────────────────────────────────────────────
   A grid row that is not full sat hard left, so nine industries in four
   columns left one tile stranded under the first column, and three insight
   cards in two columns did the same. BalancedGrid removes the gap wherever a
   column count divides the items -- nine items became three by three -- but it
   cannot always: three cards in two columns has no such count, and on a
   portrait display that is what the insights grid is.

   CSS grid has no way to centre a partial row. 1fr tracks always consume the
   free space, so justify-content has nothing to distribute, and tracks are
   integers -- one item cannot start half a column in. Flex wrap can, because
   each item carries its own width and a wrapped line centres whatever is on
   it.

   Full rows do not move. The basis is exactly (100% - gaps) / columns, so n
   items fill the line and centring them is a no-op. Only the short last row
   changes, which is the whole point.

   --grid-gap has to match each grid's own column gap, because the basis
   subtracts it.

   Two deliberate exclusions. .problem-grid takes its card rows from the grid
   with subgrid, so icon, title, problem, panel and link line up across the
   row; flex has no equivalent and converting it would undo that. And
   .matrix-features is a list of feature bullets rather than a row of cards --
   a centred orphan bullet reads as a mistake.
   ────────────────────────────────────────────────────────────────────────── */
.industry-grid, .cs-metrics, .team-card-grid,
.product-grid, .help-grid, .content-card-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
}

.industry-grid, .cs-metrics, .team-card-grid { --grid-gap: var(--space-3) }
.product-grid                                { --grid-gap: var(--space-lg) }
.help-grid                                   { --grid-gap: var(--space-5) }
.content-card-grid                           { --grid-gap: var(--space-4) }

/* .content-card-grid is not laid out by BalancedGrid -- it has always been a
   fixed three, two, one -- so it declares the counts the basis needs. */
.content-card-grid { --cols: 3; --cols-md: 2; --cols-sm: 1 }

.industry-grid > *, .cs-metrics > *, .team-card-grid > *,
.product-grid > *, .help-grid > *, .content-card-grid > * {
  flex: 0 1 calc((100% - (var(--cols, 3) - 1) * var(--grid-gap, var(--space-3))) / var(--cols, 3));
  min-width: 0;
}

@media (max-width: 1023px) {
  .content-card-grid > * {
    flex-basis: calc((100% - (var(--cols-md, 2) - 1) * var(--grid-gap)) / var(--cols-md, 2));
  }
}
@media (max-width: 767px) {
  .content-card-grid > * { flex-basis: 100% }
}

@media (max-width: 900px) {
  .industry-grid > *, .cs-metrics > *, .team-card-grid > *,
  .product-grid > *, .help-grid > * {
    flex-basis: calc((100% - (var(--cols-md, 2) - 1) * var(--grid-gap)) / var(--cols-md, 2));
  }
}
@media (max-width: 560px) {
  .industry-grid > *, .cs-metrics > * {
    flex-basis: calc((100% - (var(--cols-sm, 2) - 1) * var(--grid-gap)) / var(--cols-sm, 2));
  }
  .team-card-grid > *, .product-grid > *, .help-grid > * { flex-basis: 100% }
}
@media (max-width: 380px) {
  .industry-grid > *, .cs-metrics > * { flex-basis: 100% }
}
/* end centred trailing row */


/* ── Persistent call to action ────────────────────────────────────────────
   Appears once the hero is gone on the long detail pages, and stands down
   while the enquiry form is on screen.

   It paints its own background, so it declares --color-text* for its own
   subtree rather than inheriting whatever section it happens to be floating
   over -- the bar is fixed and the page beneath it alternates dark and light.

   Hidden is visibility plus the offset, not the offset alone: a link parked
   off-screen with visibility:visible is still a tab stop, and tabbing into a
   button nobody can see is a genuinely confusing way to lose your place.
   ────────────────────────────────────────────────────────────────────────── */
.sticky-cta{
  position:fixed; left:0; right:0; bottom:0; z-index:55;
  --color-text:#fff;
  --color-text-muted:rgba(255,255,255,.72);
  background:var(--color-ink); color:var(--color-text);
  border-top:1px solid rgba(255,255,255,.12);
  box-shadow:0 -8px 28px rgba(0,0,0,.32);
  padding:10px var(--space-3);
  padding-bottom:calc(10px + env(safe-area-inset-bottom, 0px));
  visibility:hidden;
  transform:translateY(100%);
  transition:transform .28s cubic-bezier(.4,0,.2,1), visibility 0s linear .28s;
}
.sticky-cta.is-on{
  visibility:visible;
  transform:none;
  transition:transform .28s cubic-bezier(.4,0,.2,1), visibility 0s;
}

/* One column on a phone: the label is the first thing to go, because the page
   already says what you are looking at. */
.sticky-cta-inner{
  display:flex; align-items:center; justify-content:center; gap:var(--space-2);
  max-width:1100px; margin:0 auto;
}
.sticky-cta-context{
  display:none;
  font-size:.86rem; font-weight:600; color:var(--color-text);
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.sticky-cta-btn{
  flex:1 1 auto; max-width:420px;
  display:inline-flex; align-items:center; justify-content:center;
  min-height:46px; padding:11px var(--space-3);
  border-radius:var(--radius-md);
  background:linear-gradient(135deg,var(--color-brand),#ff6b35);
  color:#fff; font-weight:700; font-size:.9rem; text-decoration:none;
  box-shadow:0 6px 18px rgba(249,134,3,.28);
}
.sticky-cta-btn:hover{filter:brightness(1.06)}

@media (min-width: 640px){
  .sticky-cta-inner{justify-content:space-between}
  .sticky-cta-context{display:block; flex:1 1 auto; min-width:0}
  .sticky-cta-btn{flex:0 0 auto}
}

@media (prefers-reduced-motion: reduce){
  .sticky-cta{transition:visibility 0s}
  .sticky-cta.is-on{transition:visibility 0s}
}

/* Printing a floating bar over the last paragraph helps nobody. */
@media print{ .sticky-cta{display:none} }
/* end persistent call to action */


/* ── Capabilities: the tail, on demand ────────────────────────────────────
   A <details> rather than a script. It works before the JS loads and for
   anyone it never reaches, and browser find-in-page opens it on a match --
   a hidden div would leave the reader searching for text they were just told
   exists.
   ────────────────────────────────────────────────────────────────────────── */
.cap-more{ margin-top:var(--space-3) }

.cap-more > summary{
  display:inline-flex; align-items:center; gap:8px;
  min-height:44px; padding:10px var(--space-3);
  border:1px solid var(--color-border); border-radius:var(--radius-md);
  font-size:.85rem; font-weight:600; color:var(--color-text);
  cursor:pointer; list-style:none; user-select:none;
}
/* Safari draws its own triangle through ::-webkit-details-marker. */
.cap-more > summary::-webkit-details-marker{ display:none }

.cap-more > summary::after{
  content:''; width:8px; height:8px; flex-shrink:0;
  border-right:2px solid currentColor; border-bottom:2px solid currentColor;
  transform:translateY(-2px) rotate(45deg);
  transition:transform .2s ease;
}
.cap-more[open] > summary::after{ transform:translateY(1px) rotate(-135deg) }

.cap-more > summary:hover{ border-color:var(--color-brand); color:var(--color-brand) }
.cap-more > summary:focus-visible{
  outline:2px solid var(--color-brand); outline-offset:2px;
}

/* One label or the other, never both. */
.cap-more .cap-more-shut{ display:none }
.cap-more[open] .cap-more-open{ display:none }
.cap-more[open] .cap-more-shut{ display:inline }

.cap-more .include-grid{ margin-top:var(--space-3) }

@media (prefers-reduced-motion: reduce){
  .cap-more > summary::after{ transition:none }
}
/* end capabilities tail */


/* .tier-grid joins the centred set. Three tiers in a two-column row left the
   third stranded bottom-left, which on a pricing table reads as a mistake
   rather than a layout. Gap matches the rule it is declared with above. */
.tier-grid{
  display:flex; flex-wrap:wrap; justify-content:center; align-content:flex-start;
  --grid-gap:var(--space-3);
}
.tier-grid > *{
  flex:0 1 calc((100% - (var(--cols, 3) - 1) * var(--grid-gap)) / var(--cols, 3));
  min-width:0;
}
@media (max-width: 900px){
  .tier-grid > *{
    flex-basis:calc((100% - (var(--cols-md, 2) - 1) * var(--grid-gap)) / var(--cols-md, 2));
  }
}
@media (max-width: 560px){
  .tier-grid > *{ flex-basis:100% }
}
/* end tier grid */


/* ── Focus phrase ─────────────────────────────────────────────────────────
   The page's primary editorial phrase. Ported from the neon gradient demo:
   solid neon letters on hover, one continuous gradient underline beneath the
   whole phrase.

   Named focus-phrase rather than neon-anything on purpose. Neon is the current
   presentation; the business concept is "this is the phrase this page is
   about", and that outlives whatever it looks like.

   Every colour, stop, duration and easing below is the demo's. Two things had
   to change, both forced by this site rather than chosen:

   1. The demo sets letters to var(--ink) at rest. Here --ink is aliased to
      --color-surface and resolves to #ffffff, so that would paint the phrase
      white on a white section. Letters inherit instead, which is also what a
      site alternating dark and light sections needs.

   2. The demo's underline is an absolutely positioned ::after on an
      inline-flex word. That cannot wrap: a five word phrase in body copy at
      390px overflows the line rather than breaking. The underline is a
      background layer here instead, so it wraps with the text and each line
      gets its own. The gradient, the stops and the reveal are unchanged; the
      neon bloom under the line is approximated by a second, softer band,
      because a background layer cannot take a box-shadow.
   ────────────────────────────────────────────────────────────────────────── */
.focus-phrase{
  --word-u1:#087f3e;
  --word-u2:#00c853;
  --word-u3:#00cbe8;
  --word-u4:#ff3f9f;
  --word-u5:#ff8600;

  display:inline;
  color:inherit;
  padding-bottom:.09em;

  background-image:
    linear-gradient(90deg,
      var(--word-u1) 0%, var(--word-u2) 22%, var(--word-u3) 46%,
      var(--word-u4) 72%, var(--word-u5) 100%),
    linear-gradient(90deg,
      rgba(57,255,20,.20) 0%, rgba(0,203,232,.16) 46%, rgba(255,63,159,.12) 100%);
  background-repeat:no-repeat, no-repeat;
  background-position:0 100%, 0 104%;
  background-size:0% max(.055em, 3px), 0% .2em;

  /*
   * .7s, where the reference demo uses .44s. Deliberate: the demo draws the
   * underline while a pointer rests on the word, so it has to keep up with a
   * hand. This draws once as the phrase arrives, unprompted, and at .44s it
   * reads as a flicker rather than as something being underlined.
   *
   * Every other number here is the demo's own: the five stops and their
   * percentages, the glow layer's three rgbas, .09em, max(.055em, 3px).
   */
  transition:background-size .7s cubic-bezier(.22,1,.36,1);
  -webkit-box-decoration-break:clone;
          box-decoration-break:clone;
}


/* The visual copy. Its twin under .sr-only carries the phrase for assistive
   technology, so this half is free to be forty single-character elements. */
.focus-phrase__visual{ display:inline }

.focus-phrase__letter{
  --letter:currentColor;
  position:relative;
  display:inline-block;
  color:inherit;
  transition:
    color .26s ease,
    transform .36s cubic-bezier(.22,1,.36,1),
    text-shadow .30s ease;
  cursor:default;
  -webkit-font-smoothing:antialiased;
}


/* The demo's four solid neon colours, cycled across the phrase. */
.fp-green { --letter:#00d85a }
.fp-cyan  { --letter:#00bfe7 }
.fp-pink  { --letter:#ff2f92 }
.fp-orange{ --letter:#ff7a00 }


@media (prefers-reduced-motion: reduce){
  .focus-phrase{ transition:none; background-size:100% max(.055em, 3px), 100% .2em }
  .focus-phrase__letter{ transition:none }
}

/* Nothing in here is interactive, and nothing should suggest it is. */
.focus-phrase, .focus-phrase *{ cursor:inherit }
/* end focus phrase */

/* ── Focus phrase: where it appears ───────────────────────────────────────
   Advice, not a gate. Every state carries a word as well as a colour, because
   a tick that is only green tells a colour-blind editor nothing.
   ────────────────────────────────────────────────────────────────────────── */
.fp-audit{
  border:1px solid var(--admin-border); border-radius:10px;
  background:var(--admin-card-bg); padding:14px 16px;
}
.fp-audit-head{
  font-size:.72rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
  color:var(--admin-text-muted); margin-bottom:10px;
}
.fp-audit-list{ list-style:none; margin:0; padding:0; display:grid; gap:7px }
.fp-audit-list li{
  display:grid; grid-template-columns:18px 1fr auto; align-items:center; gap:9px;
  font-size:.85rem; color:var(--admin-text);
}
.fp-audit-list li em{ font-style:normal; font-size:.76rem; color:var(--admin-text-muted) }
.fp-audit-list li.is-yes > span:first-child{ color:var(--admin-badge-green, #16a34a); font-weight:700 }
.fp-audit-list li.is-no  > span:first-child{ color:var(--admin-text-muted) }
.fp-audit-note{
  margin:12px 0 0; padding-top:11px; border-top:1px solid var(--admin-border);
  font-size:.82rem; line-height:1.6; color:var(--admin-text-muted);
}
/* end focus phrase audit */

/* ── Products, grouped by solution ────────────────────────────────────────
   The page was a flat grid of ten cards with no ordering a reader could infer
   and no colour anywhere. Each group now states the question it answers, and
   takes one accent from a cycled palette -- outcomes carry an icon but no
   colour of their own, so the palette lives here and the group takes one by
   index.

   Restrained on purpose: the accent lands on the mark, the rule and the count,
   not on the cards. Ten cards in six colours is a swatch book, not a
   catalogue.
   ────────────────────────────────────────────────────────────────────────── */
.product-group{ --pg: var(--color-brand); }
.product-group + .product-group{ margin-top:var(--space-6) }

.product-group-head{
  display:flex; align-items:flex-start; gap:14px;
  padding-bottom:16px; margin-bottom:var(--space-4);
  border-bottom:1px solid var(--color-border);
  position:relative;
}
/* The accent picks up the group's colour where the border stays neutral. */
.product-group-head::after{
  content:''; position:absolute; left:0; bottom:-1px; width:64px; height:2px;
  background:var(--pg); border-radius:999px;
}

.product-group-mark{
  flex:0 0 auto; width:40px; height:40px; border-radius:12px;
  display:grid; place-items:center;
  color:var(--pg);
  background:color-mix(in srgb, var(--pg) 14%, transparent);
  border:1px solid color-mix(in srgb, var(--pg) 30%, transparent);
}

.product-group-copy{ flex:1 1 auto; min-width:0 }

.product-group-name{
  margin:0; font-size:1.15rem; font-weight:700; letter-spacing:-.01em;
  color:var(--color-text);
}
.product-group-name a{ color:inherit; text-decoration:none }
.product-group-name a:hover{ color:var(--pg) }

.product-group-promise{
  margin:5px 0 0; font-size:.9rem; line-height:1.6;
  color:var(--color-text-muted); max-width:62ch;
}

.product-group-count{
  flex:0 0 auto; align-self:center;
  font-size:.74rem; font-weight:600; letter-spacing:.04em; text-transform:uppercase;
  color:var(--pg); white-space:nowrap;
}

/* Six accents, chosen to hold up on both the dark and light sections the
   rhythm alternates between. The first is the brand: this is our own
   catalogue, and it should open in our own colour. */
.pg-accent-1{ --pg:#F98603 }
.pg-accent-2{ --pg:#22c3a6 }
.pg-accent-3{ --pg:#5b8cff }
.pg-accent-4{ --pg:#c77dff }
.pg-accent-5{ --pg:#ff6b8a }
.pg-accent-6{ --pg:#3fbf6f }

/* The compare tray's chips, moved out of a JS string and into a class. */
.cmp-slot{
  display:inline-flex; align-items:center; gap:8px;
  padding:6px 14px; border-radius:100px;
  background:var(--color-surface-hover); color:var(--color-text); font-size:.78rem;
}
.cmp-slot > span{ cursor:pointer; color:var(--color-text-faint); font-size:.9rem }
.cmp-slot > span:hover{ color:var(--color-text) }

@media (max-width: 560px){
  .product-group-head{ flex-wrap:wrap }
  .product-group-count{ order:3; width:100% }
}
/* end products grouped by solution */

/* ── Industry tags on a product card ──────────────────────────────────────
   The demo's palette and motion at chip scale: each sector keeps one of six
   colours, and the continuous gradient underline sweeps in beneath the word.

   Not per-letter. "Beauty & Personal Care" is twenty characters, three tags to
   a card and ten cards to the page is roughly six hundred elements at 12px --
   a size where the colour of an individual letter cannot be seen anyway. More
   to the point, .focus-phrase means "the one phrase this page is about", and
   thirty neon phrases on the same page would leave it meaning nothing.

   Scoped to .tag--industry. .tag is used in twelve other views.
   ────────────────────────────────────────────────────────────────────────── */
.tag--industry{
  --tone: var(--color-text-muted);
  position:relative;
  background-image:linear-gradient(
    90deg,
    #087f3e 0%, #00c853 22%, #00cbe8 46%, #ff3f9f 72%, #ff8600 100%);
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:0% 2px;
  transition:
    color .26s ease,
    background-size .44s cubic-bezier(.22,1,.36,1);
}

/* Hovering the card, not each chip: the whole card is one link, and asking a
   reader to hover a 60px pill to find out what colour it is helps nobody. */
.product-card:hover .tag--industry{
  color:var(--tone);
  background-size:100% 2px;
}

/* Six tones, the demo's four plus two that hold up beside them. Each reads on
   the dark and light sections the grid alternates between. */
.ind-tone-1{ --tone:#00d85a }
.ind-tone-2{ --tone:#00bfe7 }
.ind-tone-3{ --tone:#ff2f92 }
.ind-tone-4{ --tone:#ff7a00 }
.ind-tone-5{ --tone:#8b7bff }
.ind-tone-6{ --tone:#ffc042 }

@media (hover:none){
  /* Nothing to hover, so the underline rests visible and the colour stays
     neutral. A permanent thirty-chip rainbow is what this avoids. */
  .tag--industry{ background-size:100% 2px }
}

@media (prefers-reduced-motion: reduce){
  .tag--industry{ transition:none; background-size:100% 2px }
}
/* end industry tags */

/* ── Toned links in the glance table ─────────────────────────────────────
   The sector's own colour, so it matches its chip on the product cards. The
   underline is the demo's gradient, same as the cards, revealed on hover of
   the link itself -- unlike a card, a table row is not one big target, so
   here the link is the thing you point at.
   ────────────────────────────────────────────────────────────────────────── */
.matrix-link--toned{
  background-image:linear-gradient(
    90deg,
    #087f3e 0%, #00c853 22%, #00cbe8 46%, #ff3f9f 72%, #ff8600 100%);
  background-repeat:no-repeat;
  background-position:0 100%;
  background-size:0% 2px;
  transition:
    color .26s ease,
    background-size .44s cubic-bezier(.22,1,.36,1);
}
.matrix-link--toned:hover,
.matrix-link--toned:focus-visible{
  color:var(--tone, var(--color-brand));
  background-size:100% 2px;
}

@media (hover:none){
  .matrix-link--toned{ background-size:100% 2px }
}
@media (prefers-reduced-motion: reduce){
  .matrix-link--toned{ transition:none; background-size:100% 2px }
}
/* end toned matrix links */

/* ── problem → outcome, on a dark ground ──────────────────────────────────
   .po-block and .po-block-out were drawn for a light section: a near-white
   card, and a brand tint carrying near-black text. --color-brand-tint is
   rgba(249,134,3,.1) -- a ten percent wash that takes whatever is underneath
   it -- so on a dark section the outcome panel became dark brown while its
   text stayed --c-gray-900. Dark on dark. The orange eyebrow above it kept
   working, which is what made it look like a styling choice rather than a
   fault.

   This is the surface contract this codebase already states: anything that
   paints its own background owes its subtree a text colour. These blocks paint
   two, so they owe one per ground.

   The values are the ones .pstack-copy already established for the same blocks
   on a dark section, keyed to the tone instead of to one variant's wrapper --
   so it holds wherever SectionRhythm happens to put the section. That matters:
   this only surfaced because removing the duplicate "Built for" section shifted
   the alternation and dropped this one onto dark.
   ────────────────────────────────────────────────────────────────────────── */
.dark-section .po-block{
  background:rgba(255,255,255,.05);
  border-left-color:rgba(255,255,255,.22);
}
.dark-section .po-text{ color:var(--color-text-muted) }

.dark-section .po-block-out{
  background:var(--color-brand-tint);
  border-left-color:var(--color-brand);
}
.dark-section .po-text-out{ color:var(--c-white); font-weight:500 }

.dark-section .problem-tag{
  background:rgba(255,255,255,.1);
  color:rgba(255,255,255,.8);
}
/* end problem → outcome on dark */

/* ── Product detail tabs ──────────────────────────────────────────────────
   Desktop: strip above, one panel at a time, copy beside its screenshot.
   Mobile:  panels stacked and all readable, strip stuck to the top following
            the scroll. The reader is already scrolling; a tap to reveal copy
            that is right there is asking twice.
   ────────────────────────────────────────────────────────────────────────── */
.pt{ margin-top:var(--space-4) }

.pt-strip{
  display:flex; gap:4px; overflow-x:auto; scrollbar-width:none;
  border-bottom:1px solid var(--color-border);
  position:sticky; top:0; z-index:8;
  background:var(--color-surface);
  margin-bottom:var(--space-4);
}
.pt-strip::-webkit-scrollbar{ display:none }

.pt-tab{
  flex:0 0 auto; position:relative;
  padding:13px var(--space-3); min-height:46px;
  display:inline-flex; align-items:center;
  font-size:.88rem; font-weight:600; white-space:nowrap;
  color:var(--color-text-muted); text-decoration:none;
  border:0; background:none; cursor:pointer;
  transition:color .22s ease;
}
.pt-tab::after{
  content:''; position:absolute; left:var(--space-3); right:var(--space-3); bottom:-1px;
  height:2px; border-radius:999px; background:var(--color-brand);
  transform:scaleX(0); transform-origin:center;
  transition:transform .28s cubic-bezier(.22,1,.36,1);
}
.pt-tab.is-on{ color:var(--color-text) }
.pt-tab.is-on::after{ transform:scaleX(1) }
.pt-tab:hover{ color:var(--color-text) }
.pt-tab:focus-visible{ outline:2px solid var(--color-brand); outline-offset:-2px }

.pt-panel{ display:none }
.pt-panel.is-on{ display:grid; gap:var(--space-4) }

.pt-panel-title{
  margin:0 0 var(--space-2); font-size:1.15rem; font-weight:700;
  letter-spacing:-.01em; color:var(--color-text);
}

.pt-list{ list-style:none; margin:0; padding:0; display:grid; gap:10px }
.pt-list li{
  display:grid; grid-template-columns:18px 1fr; gap:10px; align-items:start;
  font-size:.95rem; line-height:1.6; color:var(--color-text-muted);
}
.pt-list li .icon{ color:var(--color-brand); margin-top:3px }

.pt-panel-shot{ margin:0 }
.pt-panel-shot img{
  display:block; width:100%; height:auto;
  border-radius:var(--radius-lg);
  border:1px solid var(--color-border);
  box-shadow:var(--shadow-md, 0 18px 40px rgba(0,0,0,.14));
}

/* Copy beside its screenshot once there is room for both. */
@media (min-width: 900px){
  .pt-panel.is-on{ grid-template-columns:minmax(0,1fr) minmax(0,1.1fr); align-items:start }
  .pt-panel-title{ font-size:1.3rem }
}

/* Every panel readable, headings doing the separating. */
@media (max-width: 899px){
  .pt-panel{ display:block; padding-bottom:var(--space-5) }
  .pt-panel + .pt-panel{ border-top:1px solid var(--color-border); padding-top:var(--space-4) }
  .pt-panel-shot{ margin-top:var(--space-3) }
  /* Scrolled-to panels sit under the sticky strip without hiding their title. */
  .pt-panel{ scroll-margin-top:64px }
}

/* Before the script runs: every panel visible, nothing trapped. */
.no-js .pt-panel{ display:block }
/* end product detail tabs */

/* The strip is sticky, so it needs an opaque ground to sit content under --
   and painting one means owing the text a colour, which is the contract this
   codebase states. --color-surface is defined once at :root and is not
   redefined per section, so it stayed white on a dark section while the labels
   took --color-text-muted at rgba(255,255,255,.72). White on white.

   The section's own grounds instead, the same pair every other tone-aware
   surface here uses. */
.light-section .pt-strip{ background:var(--color-paper) }
.dark-section  .pt-strip{ background:var(--color-ink) }
/* end product tab strip ground */

/* A sample panel has to be unmistakable, including in a screenshot of one. */
.pt-sample{
  display:inline-block; margin-left:8px; vertical-align:middle;
  padding:2px 8px; border-radius:999px;
  background:var(--color-warning-bg, rgba(245,158,11,.16));
  border:1px solid var(--color-warning-border, rgba(245,158,11,.45));
  color:var(--color-text);
  font-size:.62rem; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
}
/* end sample flag */

/* ── Inline field errors ──────────────────────────────────────────────────
   Colour is never the only signal: a wrong field gets a message beside it and
   aria-invalid, so it reads the same to somebody who cannot see the border.
   ────────────────────────────────────────────────────────────────────────── */
.field.is-wrong{
  border-color:var(--color-danger, #dc2626);
  box-shadow:0 0 0 3px rgba(220,38,38,.14);
}
.field-error{
  margin:6px 0 0; font-size:.8rem; line-height:1.5;
  color:var(--color-danger-text, #dc2626);
}
.field-error:empty{ display:none }
/* end inline field errors */

/* ── Solutions, one row each ──────────────────────────────────────────────
   The name is always there. The promise, the services and a look at the
   software arrive on hover -- everything revealed is supplementary, and the
   row is a link whose name is the link text, so nothing that matters is
   behind the interaction.

   The preview is a product that serves the solution. The outcomes have no
   imagery of their own; their products do.
   ────────────────────────────────────────────────────────────────────────── */
.sol-list{ position:relative; margin-top:var(--space-4) }

.sol-row{
  position:static;
  display:grid; grid-template-columns:1fr auto; align-items:center; gap:var(--space-3);
  padding:var(--space-3) var(--space-2);
  border-top:1px solid var(--color-border);
  text-decoration:none; color:inherit;
  transition:background-color .28s ease, padding-left .35s cubic-bezier(.22,1,.36,1);
}
.sol-list .sol-row:last-child{ border-bottom:1px solid var(--color-border) }

.sol-row:hover,
.sol-row:focus-visible{
  background:var(--color-surface-hover);
  padding-left:var(--space-3);
  outline:none;
}
.sol-row:focus-visible{ box-shadow:inset 3px 0 0 var(--color-brand) }

.sol-row-name{
  display:inline-flex; align-items:center; gap:10px; justify-self:end;
  font-size:clamp(1.1rem, 2.4vw, 1.75rem); font-weight:700; letter-spacing:-.02em;
  color:var(--color-text); white-space:nowrap;
  transition:color .28s ease;
}
.sol-row:hover .sol-row-name{ color:var(--color-brand) }
.sol-row-name .icon{ opacity:0; transform:translateX(-6px); transition:all .3s cubic-bezier(.22,1,.36,1) }
.sol-row:hover .sol-row-name .icon,
.sol-row:focus-visible .sol-row-name .icon{ opacity:1; transform:none }

/* The supporting copy, held back until the row is the one being read. */
.sol-row-aside{
  display:flex; flex-direction:column; gap:8px; min-width:0;
  opacity:0; transform:translateY(4px);
  transition:opacity .3s ease, transform .35s cubic-bezier(.22,1,.36,1);
}
.sol-row:hover .sol-row-aside,
.sol-row:focus-visible .sol-row-aside{ opacity:1; transform:none }

.sol-row-promise{
  font-size:.95rem; line-height:1.55; color:var(--color-text-muted);
  max-width:46ch;
}
.sol-row-services{ display:flex; flex-wrap:wrap; gap:6px }
.sol-row-service{
  font-size:.72rem; font-weight:600; letter-spacing:.02em;
  padding:3px 10px; border-radius:999px;
  background:var(--color-surface-interactive); color:var(--color-text-muted);
}

/* Centred on the list, not on the row: absolute against .sol-list, which is
   the only positioned ancestor. Inside the row so no index has to be matched
   to an image in script -- the row that is hovered shows its own. */
.sol-row-shot{
  position:absolute; left:50%; top:50%; z-index:2;
  width:min(380px, 30vw); height:auto;
  border-radius:var(--radius-lg);
  border:1px solid var(--color-border);
  box-shadow:0 24px 60px rgba(0,0,0,.34);
  opacity:0; pointer-events:none;
  transform:translate(-50%,-50%) scale(.94);
  transition:opacity .28s ease, transform .45s cubic-bezier(.22,1,.36,1);
}
.sol-row:hover .sol-row-shot,
.sol-row:focus-visible .sol-row-shot{ opacity:1; transform:translate(-50%,-50%) scale(1) }

/* No hover to reveal anything, so nothing is held back and the floating
   preview goes: a picture pinned over a list is only legible when it is the
   one thing being pointed at. */
@media (max-width: 899px), (hover: none){
  .sol-row{ grid-template-columns:1fr; gap:10px; padding:var(--space-3) 0 }
  .sol-row-name{ justify-self:start; font-size:1.1rem }
  .sol-row-name .icon{ opacity:1; transform:none }
  .sol-row-aside{ opacity:1; transform:none }
  .sol-row-shot{ display:none }
  .sol-row:hover{ padding-left:0 }
}

@media (prefers-reduced-motion: reduce){
  .sol-row, .sol-row-aside, .sol-row-shot, .sol-row-name, .sol-row-name .icon{ transition:none }
  .sol-row-shot{ transform:translate(-50%,-50%) }
  .sol-row:hover .sol-row-shot{ transform:translate(-50%,-50%) }
}
/* end solutions list */

/* ── Product gallery: the caption gets out of the way ─────────────────────
   Adapted from the supplied hover study: the frame widens, the picture pushes
   in, and the words slide up and out of their own box.

   Two departures from it, both about not hiding things. The caption is legible
   at rest rather than only on hover -- it names the screen, and a reader
   scanning should not have to point at each one to find out what it is. And
   the picture scales to 1.14 rather than 2: at 2 a screenshot is a crop of
   four pixels, which is fine for a photograph and useless for a interface.
   ────────────────────────────────────────────────────────────────────────── */
.gal{ display:grid; gap:var(--space-4); margin-top:var(--space-4) }

.gal-row{
  position:relative; margin:0;
  display:grid; grid-template-columns:minmax(0,3fr) minmax(0,2fr);
  align-items:center; gap:var(--space-3);
  transition:grid-template-columns .45s cubic-bezier(.22,1,.36,1);
}

.gal-frame{
  overflow:hidden; border-radius:var(--radius-lg);
  border:1px solid var(--color-border);
  background:var(--color-surface-interactive);
  aspect-ratio:16/9;
}
.gal-frame img{
  display:block; width:100%; height:100%;
  object-fit:cover; object-position:top center;
  transition:transform .55s cubic-bezier(.22,1,.36,1);
}

.gal-label{ overflow:hidden; min-width:0 }
.gal-label-text{
  display:block;
  font-size:clamp(1.05rem, 2.2vw, 1.6rem); font-weight:700; letter-spacing:-.02em;
  color:var(--color-text);
  transition:transform .45s cubic-bezier(.22,1,.36,1), opacity .3s ease;
}

/* The frame takes the room, the picture leans in, the words leave. */
.gal-row:hover{ grid-template-columns:minmax(0,5fr) minmax(0,1fr) }
.gal-row:hover .gal-frame img{ transform:scale(1.14) }
.gal-row:hover .gal-label-text{ transform:translateY(-130%); opacity:0 }

/* Nothing to hover: the caption stays put and the frame keeps its proportions. */
@media (max-width: 899px), (hover: none){
  .gal-row{ grid-template-columns:1fr; gap:12px }
  .gal-row:hover{ grid-template-columns:1fr }
  .gal-row:hover .gal-frame img{ transform:none }
  .gal-row:hover .gal-label-text{ transform:none; opacity:1 }
  .gal-label-text{ font-size:1rem }
}

@media (prefers-reduced-motion: reduce){
  .gal-row, .gal-frame img, .gal-label-text{ transition:none }
  .gal-row:hover .gal-frame img{ transform:none }
  .gal-row:hover .gal-label-text{ transform:none; opacity:1 }
  .gal-row:hover{ grid-template-columns:minmax(0,3fr) minmax(0,2fr) }
}
/* end product gallery */

/* Letters are inline-block, which makes each one an inline-level box the
   browser may break after. Grouping them into words and holding the word
   together is what stops "management" wrapping as "managemen" / "t" -- which
   only ever showed on a narrow screen, because a phrase that fits on one line
   has nothing to break. */
.focus-phrase__word{ white-space:nowrap }
/* end focus phrase word grouping */

/* ── Product tabs: sit with the rest of the page ──────────────────────────
   Four corrections after looking at it in a browser rather than at the
   markup.

   1. A panel with no image was still a two column grid, so half the section
      was blank and the copy was crammed into the left 47%. Six of ten
      products have no screenshots, so that is the common case rather than
      the edge one.

   2. The panel repeated its tab's label directly under it. On a desktop only
      one panel shows and the strip is right above, so the heading said
      nothing twice. It stays below the breakpoint, where the panels stack
      and a heading is what separates them.

   3. The section head is centred and the copy was hard left. A single column
      panel now takes a reading measure and centres in the container, which
      is what every other prose section on the site does.

   4. The strip was a flat opaque slab. It has to be opaque enough to sit
      content under while stuck, so it borrows what the scrolled nav does:
      translucent with a blur behind it.
   ────────────────────────────────────────────────────────────────────────── */

/* 4 — the strip, softened. */
.light-section .pt-strip{ background:rgba(255,255,255,.86) }
.dark-section  .pt-strip{ background:rgba(15,15,23,.82) }
.pt-strip{
  -webkit-backdrop-filter:blur(14px) saturate(160%);
          backdrop-filter:blur(14px) saturate(160%);
}

/* 1 and 3 — no image, no second column, and a measure worth reading. */
@media (min-width: 900px){
  .pt-panel--noshot.is-on{ grid-template-columns:minmax(0,1fr) }
  .pt-panel--noshot .pt-panel-copy{ max-width:68ch; margin-inline:auto }

  /* 2 — the tab above already names it. */
  .pt-panel-title{ position:absolute; width:1px; height:1px; overflow:hidden;
                   clip-path:inset(50%); white-space:nowrap }
}

/* Below the breakpoint the panels stack, so the heading is doing real work
   and the copy has nowhere else to go. */
@media (max-width: 899px){
  .pt-panel-copy{ max-width:none; margin-inline:0 }
}
/* end product tab alignment */

/* ── The logo's backing ───────────────────────────────────────────────────
   The mark is not tone-neutral: the lower half of the P and the "prabhabi
   infocom" wordmark are solid black, so on a dark ground they disappear and
   only the orange half survives. That is what the white tile is for, and it
   cannot simply be dropped.

   What it can stop being is a bare white rectangle cut out of the hero. A
   hairline and a soft shadow make it read as a badge that belongs there.

   The real fix is an asset: a light-on-dark logo would let the backing go
   entirely on dark grounds. There is no such file today -- logo-icon.png and
   logo-full.png both carry the black elements.
   ────────────────────────────────────────────────────────────────────────── */
.brand-mark-wrap{
  border:1px solid rgba(255,255,255,.55);
  box-shadow:0 2px 10px rgba(0,0,0,.22);
}
/* On the light bar the ring would outline white on white, so it goes. */
.nav.scrolled .brand-mark-wrap{
  border-color:rgba(15,15,23,.10);
  box-shadow:none;
}
/* end logo backing */

/* The media list is a list now: see the image, remove the image. Everything
   that used to hang off each row moved to the Tab images picker, where the
   pairing is set against the product's own text. */
.admin-gallery-row--plain{
  display:grid; grid-template-columns:56px 1fr auto; gap:14px; align-items:center;
  padding:10px 12px; border:1px solid var(--admin-border); border-radius:10px;
  background:var(--admin-card-bg);
}
.admin-gallery-row--plain + .admin-gallery-row--plain{ margin-top:8px }
.admin-gallery-row--plain img{
  width:56px; height:40px; object-fit:cover; border-radius:6px; display:block;
  border:1px solid var(--admin-border);
}
.admin-gallery-row--plain .admin-gallery-row-name{ display:grid; gap:2px; min-width:0 }
.admin-gallery-row--plain code{
  font-size:.72rem; color:var(--admin-text-muted);
  overflow:hidden; text-overflow:ellipsis;
}
.admin-gallery-row--plain .admin-media-remove{
  display:inline-flex; align-items:center; gap:7px;
  font-size:.8rem; color:var(--admin-text-muted); white-space:nowrap;
}
/* end plain media list */

/* ── Admin tables that were never wrapped ─────────────────────────────────
   .admin-table-wrap exists and half the admin views use it. The other half
   put .admin-table straight inside .admin-card, where a table with eight
   fixed-width columns is a good deal wider than a phone and takes the page
   with it.

   Catching it on the card means the ten views that missed the wrapper are
   fixed without editing ten files, and a view that already has the wrapper is
   unaffected -- it just scrolls at the wrapper instead.

   1023 rather than 767: these tables are wide enough to overflow a tablet too.
   ────────────────────────────────────────────────────────────────────────── */
@media (max-width: 1023px){
  .admin-card:has(> .admin-table){
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
  }
}
/* end admin table overflow */

/* ── Focus phrase: lit on arrival, not on hover ───────────────────────────
   The effect moved off hover. A highlight exists to stop somebody reading, and
   hover cannot do that -- it needs the reader to already be pointing at the
   word they have not noticed yet. On a phone it never fired at all.

   So the underline is drawn as the phrase arrives and the letters take their
   colours in sequence behind it. One phrase per block keeps it a signal;
   Highlight decides which one.

   These override the hover rules further up rather than replacing them: the
   component is still used in a couple of places that pass an explicit phrase,
   and a rest state that only appears on hover would leave those blank.
   ────────────────────────────────────────────────────────────────────────── */
.focus-phrase.is-lit{
  background-size:100% max(.055em, 3px), 100% .2em;
}

/* Letters arrive left to right, a beat behind the line. */
.focus-phrase__letter{
  color:inherit;
  transition:color .34s ease, text-shadow .34s ease;
}
.focus-phrase.is-lit .focus-phrase__letter{
  color:var(--letter);
  text-shadow:
    0 0 1px currentColor,
    0 0 4px color-mix(in srgb, var(--letter) 34%, transparent),
    0 0 8px color-mix(in srgb, var(--letter) 18%, transparent);
}


/* Nothing observed it, or the script never ran: show it rather than hide it.
   A highlight that depends on JavaScript is a highlight that sometimes is not
   one, and the underline is the whole point. */
.no-js .focus-phrase,
.focus-phrase.is-static{
  background-size:100% max(.055em, 3px), 100% .2em;
}

@media (prefers-reduced-motion: reduce){
  .focus-phrase, .focus-phrase__letter{ transition:none }
  .focus-phrase{ background-size:100% max(.055em, 3px), 100% .2em }
}
/* end focus phrase lit */




/* ============================================================================
   UNLOCK GROWTH -- the landing page
   ----------------------------------------------------------------------------
   Laid out from the supplied design. Problem, failed attempts, why, us, price,
   evidence, conversation.

   Colour comes from tokens only, so a palette change reaches this page like it
   reaches every other. The surface tokens are emitted per section now, so
   --color-text and friends can be used directly here -- they resolve against
   the ground the element is standing on. Anything painting its own background
   still owes its subtree a text colour, and says so with .on-light.
   ========================================================================= */

/* --------------------------------------------------------------- 01 hero */

.ug-hero {
  display: grid;
  gap: var(--space-2xl);
  grid-template-columns: 1fr;
  align-items: center;
}

@media (min-width: 960px) {
  .ug-hero { grid-template-columns: 1.15fr 0.85fr; }
}

/* The words the design sets in brand orange. The bright brand rather than the
   darkened text variant, because these land on ink. */
.ug-accent { color: var(--color-brand); }

.ug-hero-line {
  margin: var(--space-lg) 0 0;
  font-family: var(--font-heading);
  font-size: clamp(1.2rem, 2.6vw, 1.75rem);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
}

.ug-hero-lead { margin: var(--space-lg) 0 0; max-width: 40rem; }
.ug-hero .cta-actions { margin-top: var(--space-xl); }

/* Facts, not claims. The row renders only what is recorded, so it may hold
   one, two or three -- and has to look deliberate at any of them. */
.ug-stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xl);
  margin: var(--space-2xl) 0 0;
  padding: var(--space-lg) 0 0;
  border-top: 1px solid var(--color-hairline);
  list-style: none;
}

.ug-stat-n {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 800;
  line-height: 1;
  color: var(--color-brand);
}

.ug-stat-k {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8rem;
  line-height: 1.35;
  color: var(--color-text-muted);
}

.ug-hero-panel {
  padding: clamp(1.3rem, 2.6vw, 2rem);
  border-radius: var(--radius-xl);
  background: var(--color-glass-fill);
  border: 1px solid var(--color-glass-border);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
}

.ug-hero-panel-k {
  display: block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.ug-outcomes {
  display: grid;
  gap: var(--space-md);
  margin: var(--space-lg) 0;
  padding: 0;
  list-style: none;
}

.ug-outcomes li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.8rem;
  align-items: start;
}

.ug-outcome-icon,
.ug-prob-icon,
.ug-pillar-icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: var(--color-brand-tint);
  border: 1px solid var(--color-brand-line);
  color: var(--color-brand);
  flex: none;
}

.ug-outcomes strong {
  display: block;
  font-size: 0.98rem;
  color: var(--color-text-bright);
}

.ug-outcomes em {
  display: block;
  margin-top: 0.15rem;
  font-style: normal;
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--color-text-muted);
}

.ug-hero-panel-foot {
  margin: 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-hairline);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

/* An admin-only note: says what is missing and which field fills it, standing
   where the missing thing would have gone. */
.ug-note {
  max-width: 46rem;
  margin: var(--space-lg) 0 0;
  padding: var(--space-md);
  border-radius: var(--radius-md);
  border: 1px dashed var(--color-brand-line);
  background: var(--color-brand-tint);
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--color-text);
}

.ug-note code {
  font-family: var(--font-mono);
  font-size: 0.95em;
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: var(--color-surface-hover);
}

/* ------------------------------------------------------- 02 problem rail */

.ug-rail { margin-top: var(--space-xl); }

/* All five side by side, scrolling when they do not fit. Scroll rather than a
   transform, so a touch screen gets swiping for free and the row is still
   usable with the script off. */
.ug-rail-track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(13rem, 1fr);
  gap: var(--space-md);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  padding-bottom: var(--space-xs);
  scrollbar-width: none;
}

.ug-rail-track::-webkit-scrollbar { display: none; }

@media (prefers-reduced-motion: reduce) {
  .ug-rail-track { scroll-behavior: auto; }
}

.ug-prob {
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding: var(--space-md);
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  transition: transform var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}

.ug-prob:hover {
  transform: translateY(-3px);
  border-color: var(--color-brand-line);
}

.ug-prob-n {
  margin-top: var(--space-md);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--color-text-faint);
}

.ug-prob-title {
  margin: 0.35rem 0 0;
  font-family: var(--font-heading);
  font-size: 1.08rem;
  font-weight: 700;
  line-height: 1.28;
  color: var(--color-text-bright);
}

.ug-prob-body {
  margin: var(--space-sm) 0 var(--space-lg);
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--color-text-muted);
}

.ug-prob-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: auto 0 0;
  padding: 0;
  list-style: none;
}

.ug-prob-tags li {
  padding: 0.28rem 0.65rem;
  font-size: 0.72rem;
  font-weight: 600;
  border-radius: var(--radius-button);
  background: var(--color-brand-tint);
  color: var(--color-brand-text);
  border: 1px solid var(--color-brand-line);
}

.ug-rail-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-md);
  margin-top: var(--space-lg);
}

/* Nothing to steer when every card is already visible. */
.ug-rail.is-static .ug-rail-controls { display: none; }

.ug-rail-btn {
  display: inline-grid;
  place-items: center;
  width: 44px;                      /* a real tap target, not a decoration */
  height: 44px;
  border-radius: 50%;
  cursor: pointer;
  color: var(--color-text);
  background: var(--color-surface-interactive);
  border: 1px solid var(--color-border);
  transition: background var(--dur-fast) var(--ease-standard),
              border-color var(--dur-fast) var(--ease-standard);
}

.ug-rail-btn:hover {
  background: var(--color-brand-tint);
  border-color: var(--color-brand-line);
}

.ug-rail-dots { display: flex; gap: 0.25rem; }

/* 8px of paint inside a 44px target. Small dots are the usual way a carousel
   becomes unusable on a phone, and the fix is padding, not a bigger dot. */
.ug-dot {
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
}

.ug-dot-fill {
  display: block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border-strong);
  transition: width var(--dur-base) var(--ease-emphasized),
              background var(--dur-base) var(--ease-standard);
}

.ug-dot[aria-current="true"] .ug-dot-fill {
  /* §19: 24px wide, 4px radius. Not a pill -- the reference draws a bar. */
  width: 24px;
  border-radius: 4px;
  background: var(--color-brand);
}

.ug-dot:hover .ug-dot-fill { background: var(--color-brand); }

/* ------------------------------------------------------ 03 what you tried */

/* Heading on the left, the supporting paragraph beside it rather than under
   it -- as in the design, and it stops the paragraph reading as a subtitle. */
.ug-split-head {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: 1fr;
  align-items: end;
  margin-bottom: var(--space-xl);
}

@media (min-width: 900px) {
  .ug-split-head { grid-template-columns: 1.1fr 0.9fr; gap: var(--space-2xl); }
}

.ug-split-lead {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.7;
  color: var(--color-text-muted);
}

.ug-tried {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
}

.ug-tried-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
}

.ug-tried-shot {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--color-surface-hover);
}

.ug-tried-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* No photograph supplied. An honest empty frame beats a stock image that
   claims to be somebody's campaign. */
.ug-tried-shot--none {
  display: grid;
  place-items: center;
  color: var(--color-brand);
  background:
    radial-gradient(120% 100% at 50% 0%, var(--color-brand-tint), transparent 70%),
    var(--color-surface-hover);
}

.ug-tried-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-lg);
}

.ug-tried-title {
  margin: 0 0 var(--space-md);
  font-family: var(--font-heading);
  font-size: 1.08rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-text-bright);
}

.ug-tried-n {
  display: block;
  margin-bottom: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.14em;
  color: var(--color-text-faint);
}

.ug-misses {
  display: grid;
  gap: 0.55rem;
  margin: 0 0 var(--space-lg);
  padding: 0;
  list-style: none;
}

.ug-misses li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.55rem;
  align-items: start;
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* The cross is the only red on the page, and it describes a method rather
   than the reader. Always paired with text, never carrying meaning alone. */
.ug-misses svg {
  margin-top: 0.28rem;
  color: var(--color-danger);
  flex: none;
}

.ug-tried-line {
  margin: auto 0 0;
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-hairline);
  font-family: var(--font-heading);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.45;
  color: var(--color-text-bright);
}

/* --------------------------------------------------------- 04 transition */

.ug-turn { max-width: 52rem; margin-inline: auto; text-align: center; }
.ug-turn-lead { margin: var(--space-lg) auto 0; max-width: 44rem; }
.ug-turn .cta-actions { margin-top: var(--space-xl); justify-content: center; }

/* ------------------------------------------------------- 05 why prabhabi */

.ug-why {
  display: grid;
  gap: var(--space-2xl);
  grid-template-columns: 1fr;
  align-items: start;
}

@media (min-width: 900px) {
  .ug-why { grid-template-columns: 1.05fr 0.95fr; }
}

.ug-pillars {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  margin: 0;
  padding: 0;
  list-style: none;
}

.ug-pillars li {
  padding: var(--space-lg);
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
}

.ug-pillars strong {
  display: block;
  margin-top: var(--space-md);
  font-family: var(--font-heading);
  font-size: 1rem;
  color: var(--color-text-bright);
}

.ug-pillars em {
  display: block;
  margin-top: 0.25rem;
  font-style: normal;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

/* The working process as one sequence. Arrows are decorative and are not read
   aloud; the order survives as an <ol> with the stylesheet off. */
.ug-chain {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: var(--space-xl) 0 0;
  padding: 0;
  list-style: none;
}

.ug-chain li {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.8rem;
  font-size: 0.85rem;
  font-weight: 600;
  border-radius: var(--radius-button);
  background: var(--color-surface-hover);
  border: 1px solid var(--color-border);
}

.ug-chain--flow li:not(:last-child)::after {
  content: "\2192";
  margin-left: 0.1rem;
  color: var(--color-text-faint);
  speak: never;
}

/* ---------------------------------------------------------- 06 packages */

.ug-pkgs {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(15.5rem, 1fr));
  margin-top: var(--space-xl);
  align-items: stretch;
}

.ug-pkg {
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  transition: transform var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard),
              box-shadow var(--dur-base) var(--ease-standard);
}

.ug-pkg:hover {
  transform: translateY(-3px);
  border-color: var(--color-brand-line);
  box-shadow: var(--shadow-card);
}

/* Featured is marked three ways -- border, shadow and a written flag --
   because a card distinguished by colour alone is not distinguished at all
   for somebody who cannot see the difference. */
.ug-pkg.is-featured {
  border-color: var(--color-brand);
  box-shadow: var(--shadow-glow, var(--shadow-card));
}

/* The design gives each plan its own header colour. Decorative only: every
   plan is also told apart by its name, its price and its list. Fixed hues
   rather than palette tokens, because the point is that they differ from each
   other, not that they match the brand. */
.ug-pkg-head {
  padding: var(--space-md) var(--space-lg);
  text-align: center;
}

/*
 * One accent, not four hues. The mockup gave each plan its own green, blue,
 * purple and orange header; design-system-v3 is two surfaces and one accent,
 * and four unrelated colours appear nowhere in it. The plans are told apart
 * by name, price, list, and -- for the featured one -- the accent and a
 * heavier border, which is what the border scale is for.
 */
.ug-pkg-head {
  background: var(--color-surface-hover);
  border-bottom: var(--b-hairline) solid var(--b-subtle);
}

.ug-pkg-name { color: var(--color-text-bright); }

.ug-pkg.is-featured .ug-pkg-head {
  background: var(--gradient-brand);
  border-bottom-color: transparent;
}

.ug-pkg.is-featured .ug-pkg-name { color: #fff; }

/* Border weight is the third signal, after the flag and the accent. */
.ug-pkg.is-featured { border-width: var(--b-regular); }

.ug-pkg-name {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.ug-pkg-inner {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--space-lg);
}

.ug-pkg-flag {
  position: absolute;
  top: var(--space-sm);
  right: var(--space-sm);
  z-index: 1;
  padding: 0.25rem 0.7rem;
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  border-radius: var(--radius-button);
  background: #fff;
  color: #16161f;
  box-shadow: 0 2px 8px rgba(0,0,0,.25);
}

.ug-pkg-summary {
  margin: 0;
  font-size: 0.88rem;
  line-height: 1.55;
  color: var(--color-text-muted);
}

.ug-pkg-price {
  margin: var(--space-lg) 0;
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-hairline);
}

.ug-pkg-amount {
  display: block;
  font-family: var(--font-heading);
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--color-text-bright);
}

.ug-pkg-amount--quoted { font-size: clamp(1.4rem, 2.6vw, 1.8rem); }

.ug-pkg-period {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}

.ug-pkg-intl {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--color-text-faint);
}

.ug-pkg-list {
  display: grid;
  gap: 0.6rem;
  margin: 0 0 var(--space-lg);
  padding: 0;
  list-style: none;
}

.ug-pkg-list li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.55rem;
  align-items: start;
  font-size: 0.88rem;
  line-height: 1.5;
}

.ug-pkg-list li svg {
  margin-top: 0.18rem;
  color: var(--color-success);
  flex: none;
}

.ug-pkg-commit {
  margin: 0 0 var(--space-md);
  font-size: 0.76rem;
  color: var(--color-text-faint);
}

/* Pushed to the foot so every CTA in the row lines up, whatever the card
   above it happens to contain. */
.ug-pkg-cta { margin-top: auto; width: 100%; justify-content: center; }

.ug-disclaimer {
  max-width: 46rem;
  margin: var(--space-xl) auto 0;
  text-align: center;
  font-size: 0.82rem;
  line-height: 1.6;
  color: var(--color-text-faint);
}

/* ----------------------------------------------------------- 07 results */

.ug-strip {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  margin: var(--space-xl) 0 0;
  padding: 0;
  list-style: none;
}

.ug-strip li {
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  background: var(--color-surface-raised);
}

.ug-strip-k {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--color-text-bright);
}

.ug-strip-v {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--color-text-muted);
}

.ug-quote {
  max-width: 42rem;
  margin: var(--space-xl) auto 0;
  padding: var(--space-xl);
  border-radius: var(--radius-xl);
  background: var(--color-surface-raised);
  border: 1px solid var(--color-border);
  text-align: center;
}

.ug-quote blockquote {
  margin: 0;
  font-family: var(--font-heading);
  font-size: clamp(1.05rem, 2vw, 1.25rem);
  line-height: 1.55;
  color: var(--color-text-bright);
}

.ug-quote blockquote::before { content: "\201C"; }
.ug-quote blockquote::after  { content: "\201D"; }

.ug-quote figcaption { margin-top: var(--space-md); font-size: 0.85rem; }
.ug-quote figcaption strong { display: block; color: var(--color-text-bright); }
.ug-quote figcaption span { color: var(--color-text-muted); }

.ug-cases {
  display: grid;
  gap: var(--space-lg);
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  margin-top: var(--space-xl);
}

.ug-case {
  display: flex;
  flex-direction: column;
  padding: var(--space-lg);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border);
  background: var(--color-surface-raised);
  text-decoration: none;
  color: inherit;
  transition: transform var(--dur-base) var(--ease-standard),
              border-color var(--dur-base) var(--ease-standard);
}

.ug-case:hover { transform: translateY(-3px); border-color: var(--color-brand-line); }

.ug-case-k {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-brand-text);
}

.ug-case-title {
  margin: var(--space-xs) 0 var(--space-md);
  font-family: var(--font-heading);
  font-size: 1.08rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--color-text-bright);
}

.ug-case-go {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: auto;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--color-brand-text);
}

/* The admin-only before/after brief. Deliberately looks unfinished: anything
   here that looked like a finished figure would be one forgotten cleanup away
   from reading as a customer claim. */
.ug-proto {
  margin-top: var(--space-xl);
  padding: var(--space-lg);
  border-radius: var(--radius-xl);
  border: 2px dashed var(--color-brand-line);
  background: var(--color-surface-hover);
}

.ug-proto-note {
  margin: 0 0 var(--space-lg);
  font-size: 0.84rem;
  line-height: 1.6;
  color: var(--color-text);
}

.ug-metrics {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}

.ug-metric {
  padding: var(--space-md);
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  background: var(--color-surface-raised);
  text-align: center;
}

.ug-metric-k {
  display: block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.ug-metric-v {
  display: block;
  margin: 0.4rem 0;
  font-family: var(--font-mono);
  font-size: 1.2rem;
  color: var(--color-text-faint);
}

.ug-metric-v strong { color: var(--color-text); }
.ug-metric-v em { font-style: normal; }

.ug-metric-g {
  display: block;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--color-text-faint);
}

/* ------------------------------------------------------ 08 consultation */

.ug-consult {
  display: grid;
  gap: var(--space-2xl);
  grid-template-columns: 1fr;
  align-items: start;
}

@media (min-width: 900px) {
  .ug-consult { grid-template-columns: 0.9fr 1.1fr; }
}

.ug-assure {
  display: grid;
  gap: 0.6rem;
  margin: var(--space-xl) 0 0;
  padding: 0;
  list-style: none;
}

.ug-assure li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.92rem;
  font-weight: 500;
}

.ug-assure svg { color: var(--color-brand); flex: none; }

/* The form paints white inside a dark band, so it carries .on-light in the
   markup: that is what re-points --color-text and friends at the light values
   for its subtree. Without it the fields would inherit the dark section's
   white text onto a white ground. */
.ug-form {
  padding: clamp(1.4rem, 2.8vw, 2.2rem);
  border-radius: var(--radius-xl);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
}

.ug-form-row {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: 1fr;
  margin-bottom: var(--space-md);
}

@media (min-width: 560px) {
  .ug-form-row { grid-template-columns: 1fr 1fr; }
}

.ug-field { margin-bottom: var(--space-md); }
.ug-form-row .ug-field { margin-bottom: 0; }

.ug-field-label { display: flex; align-items: center; gap: 0.4rem; }
.ug-field-label svg { color: var(--color-brand); flex: none; }

.ug-req { color: var(--color-danger); }

.ug-opt {
  margin-left: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-faint);
}

.ug-form select.field {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 20px) 55%, calc(100% - 14px) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.6rem;
}

.ug-form-submit { width: 100%; justify-content: center; margin-top: var(--space-sm); }

.ug-form-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  margin: var(--space-md) 0 0;
  font-size: 0.78rem;
  line-height: 1.55;
  text-align: center;
  color: var(--color-text-faint);
}

/* --------------------------------------------------------------- narrow */

@media (max-width: 560px) {
  .ug-rail-controls { gap: var(--space-xs); }
  .ug-rail-track { grid-auto-columns: minmax(84%, 1fr); }
  .ug-stats { gap: var(--space-lg); }
}

/* ============================================================================
   BORDER SYSTEM  (design-system-v3 §14)
   ----------------------------------------------------------------------------
   The one part of the system that was never implemented. Every token and
   utility below scored zero occurrences in this file before now, which is why
   the admin grids and the light-section cards read as floating text: they were
   separated by shadow and whitespace alone, and shadow does not survive being
   looked at quickly.

   Two directions, as the file specifies:

     REFINED (.b-r-*)  a structural hairline scale that sits quietly under the
                       shadow system. This is the default.

     DOMINANT (.b-dom) thick border plus a hard, un-blurred offset shadow. A
                       deliberate graphic statement for the rare moment that
                       should stand out -- a featured plan, a hero stat. Never
                       the everyday default; the moment it is used twice on a
                       page it stops meaning anything.

   The colours are surface-dependent and are emitted per section by
   ThemeService, the same way --color-text-* is. So .b-r-1 works on either
   ground with no override rule, and the .dark-section .b-r-* pairs the design
   system writes by hand are unnecessary here rather than missing.
   ========================================================================= */

:root {
  /* widths -- not surface-dependent */
  --b-hairline: 1px;
  --b-thin:     1.5px;
  --b-regular:  2px;
  --b-heavy:    3px;

  /*
   * Ground-dependent colours. These are the light values; ThemeService emits
   * them again on .light-section / .dark-section / .on-light / .on-dark, so an
   * element gets the pair for whatever it is standing on. They are declared
   * here too so the utilities still work if no palette is active.
   */
  --b-subtle:  rgba(17, 24, 41, .08);
  --b-default: rgba(17, 24, 41, .14);
  --b-strong:  rgba(17, 24, 41, .22);

  /* the named dark pair, kept under the design system's own names */
  --b-subtle-dark:  rgba(255, 255, 255, .09);
  --b-default-dark: rgba(255, 255, 255, .16);
  --b-strong-dark:  rgba(255, 255, 255, .28);

  --b-accent: var(--color-brand);
  /* .4 of the brand, derived rather than typed, so a palette change reaches it */
  --b-accent-soft: color-mix(in srgb, var(--color-brand) 40%, transparent);

  --b-dom-width:    3px;
  --b-dom-width-lg: 4px;
  --b-dom-offset:   6px;
  --b-dom-color:    #060608;
}

/* Older engines that do not know color-mix fall back to the brand line token,
   which is the same hue at a lower alpha. Better slightly softer than absent. */
@supports not (color: color-mix(in srgb, red 40%, transparent)) {
  :root { --b-accent-soft: var(--color-brand-line); }
}

/* ---------------------------------------------------------------- refined */

.b-r-1 { border: var(--b-hairline) solid var(--b-subtle); }
.b-r-2 { border: var(--b-thin)     solid var(--b-default); }
.b-r-3 { border: var(--b-regular)  solid var(--b-strong); }
.b-r-4 { border: var(--b-heavy)    solid var(--b-strong); }

.b-r-accent { border: var(--b-thin) solid var(--b-accent-soft); }

.b-r-divider {
  border: none;
  border-top: var(--b-hairline) solid var(--b-subtle);
}

/* --------------------------------------------------------------- dominant */

/*
 * The offset shadow is written out here rather than stored as a token.
 *
 * A custom property containing var() is resolved where the property is
 * DECLARED, not where it is used -- so a --b-dom-shadow defined at :root would
 * bake in the light --b-dom-color and keep it on dark ground, silently. The
 * design system names such a token; composing it at the point of use is the
 * same result without the trap.
 */
.b-dom {
  border: var(--b-dom-width) solid var(--b-dom-color);
  box-shadow: var(--b-dom-offset) var(--b-dom-offset) 0 0 var(--b-dom-color);
  transition: transform var(--dur-fast) var(--ease-standard),
              box-shadow var(--dur-fast) var(--ease-standard);
}

.b-dom:hover {
  transform: translate(-2px, -2px);
  box-shadow: calc(var(--b-dom-offset) + 3px) calc(var(--b-dom-offset) + 3px) 0 0 var(--b-dom-color);
}

.b-dom:active {
  transform: translate(1px, 1px);
  box-shadow: 2px 2px 0 0 var(--b-dom-color);
}

.b-dom-accent {
  border-color: var(--color-brand);
  box-shadow: var(--b-dom-offset) var(--b-dom-offset) 0 0 var(--color-brand);
}

.b-dom-accent:hover {
  box-shadow: calc(var(--b-dom-offset) + 3px) calc(var(--b-dom-offset) + 3px) 0 0 var(--color-brand);
}

@media (prefers-reduced-motion: reduce) {
  .b-dom, .b-dom:hover, .b-dom:active { transition: none; transform: none; }
}

/* ============================================================================
   APPLYING IT -- website cards
   ========================================================================= */

/*
 * A light-section card had border:none and was held up by --depth-neu alone.
 * On a white page that is a shadow against white, which is a card you have to
 * squint at. The hairline does the structural job and the shadow keeps doing
 * the tactile one.
 */
.light-section .card,
.light-section .surface-card,
.light-section .industry-tile,
.light-section .post-card,
.light-section .team-card,
.light-section .image-card,
.light-section .product-row-card {
  border: var(--b-hairline) solid var(--b-subtle);
}

/*
 * The hover state used to set border-color:transparent, which was harmless
 * when there was no border and is a border blinking off the moment you point
 * at it now. It firms up instead.
 */
.light-section .card:hover,
.light-section .surface-card:hover {
  border-color: var(--b-default);
}

/* ============================================================================
   APPLYING IT -- admin data grids
   ========================================================================= */

/*
 * The grid had a hairline under each row in --color-hairline (7% ink) and
 * nothing else: no outer edge, no column separation, no weight on the header.
 * Sixteen industries then read as one continuous block of text.
 */
.admin-table-wrap {
  border: var(--b-hairline) solid var(--b-subtle);
  border-radius: var(--rad-2, 16px);
  /*
   * auto, not hidden. A wide table has to be reachable; hidden does not
   * stop it being wide, it removes the part you cannot see. The radius
   * still clips.
   */
  overflow-x: auto;
}

/*
 * The table itself gets no box. It sits inside .admin-card, which already
 * draws a border and 28px of padding, and a second rounded rectangle inside
 * the first is the confusion being fixed rather than the fix. The lines
 * below are what make a row scannable; the card is what contains it.
 *
 * border-collapse stays at its original 'collapse' for the same reason it
 * always was: adjacent cell borders merge into one line instead of two.
 */

/* The header is the anchor: a real rule under it, not another hairline. */
.admin-table th {
  border-bottom: var(--b-regular) solid var(--b-default);
}

.admin-table td {
  border-bottom: var(--b-hairline) solid var(--b-subtle);
}

/*
 * Column separation, which is what actually makes a wide row scannable -- the
 * eye needs to know where "Products" stops and "Services" starts. Kept to the
 * faintest step so it guides rather than draws a spreadsheet.
 */
.admin-table th + th,
.admin-table td + td {
  border-left: var(--b-hairline) solid var(--b-subtle);
}

.admin-table tbody tr:last-child td { border-bottom: none; }

/* ============================================================================
   APPLYING IT -- admin data entry
   ========================================================================= */

/*
 * A long form was one white expanse with labelled inputs in it. The section is
 * the unit a person actually works in, so the section gets an edge and the
 * accent bar on its heading stops being the only thing marking a boundary.
 */
.admin-form-section {
  border: var(--b-hairline) solid var(--b-subtle);
  border-radius: var(--rad-2, 16px);
  padding: var(--space-3, 24px);
  margin-bottom: var(--space-3, 24px);
}

/* It used to mark its end with a bottom rule; the box does that now. */
.admin-form-section:last-child {
  border: var(--b-hairline) solid var(--b-subtle);
  margin-bottom: 0;
  padding-bottom: var(--space-3, 24px);
}

/*
 * A form section inside a card is the same nesting problem as the table, so
 * inside one it keeps the rule it always had and the box is dropped.
 * Standalone form pages, which have no card around them, get the box.
 */
.admin-card .admin-form-section {
  border: none;
  border-bottom: var(--b-hairline) solid var(--b-subtle);
  border-radius: 0;
  padding: 0 0 var(--space-3, 24px);
}
.admin-card .admin-form-section:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

/*
 * Inside a drawer the fieldsets are the only grouping there is -- there are no
 * section headings to hang an edge on -- so they are separated instead of
 * boxed. Boxes inside a 620px drawer would nest three borders deep.
 */
.admin-drawer .admin-fieldset + .admin-fieldset,
.admin-drawer .admin-fieldset + .admin-form-group,
.admin-drawer .admin-form-group + .admin-fieldset {
  padding-top: var(--space-3, 24px);
  border-top: var(--b-hairline) solid var(--b-subtle);
}

/* The drawer already carries its own edge; it does not need a boxed section. */
.admin-drawer .admin-form-section {
  border: none;
  border-radius: 0;
  padding: 0;
}

/* Inputs move onto the system's own scale rather than an admin-only one. */
.admin-form-control {
  border-width: var(--b-thin);
}


/* ============================================================================
   ADMIN — horizontal overflow on small screens
   ----------------------------------------------------------------------------
   A phone should never scroll the admin sideways. Reported on the Add Service
   drawer and on Industries.
   ========================================================================= */

/*
 * Any card holding a table becomes that table's scroll container. :has() rather
 * than a wrapper div in twenty views -- and it covers tables added later, which
 * a hand-edited wrapper would not.
 */
.admin-card:has(> .admin-table),
.admin-card:has(> .admin-table-wrap) {
  overflow-x: auto;
}

/* Nothing in the admin may make the document itself wider than the screen. */
.admin-main,
.admin-content {
  max-width: 100%;
  min-width: 0;
}

@media (max-width: 767px) {
  /*
   * Inline grid-template-columns beats every media query in this file, so
   * nineteen admin views stay multi-column on a phone and the ones with a
   * fixed px track cannot shrink at all. !important is the only thing that
   * reaches an inline style; one column is the right answer for all of them
   * at this width.
   */
  .admin-content [style*="grid-template-columns"],
  .admin-drawer [style*="grid-template-columns"] {
    grid-template-columns: 1fr !important;
  }

  /* Grid and flex children default to min-width:auto, which refuses to shrink
     below their content. That is how one long unbroken string widens a page. */
  .admin-content > *,
  .admin-drawer .admin-fieldset > *,
  .admin-form-group { min-width: 0; }

  .admin-form-group input,
  .admin-form-group select,
  .admin-form-group textarea,
  .admin-form-control { max-width: 100%; }
}

/*
 * With a drawer open the page behind it must not scroll. Otherwise the drawer
 * stays pinned to the viewport while its own backdrop slides out from under it,
 * which is the second of the two screenshots.
 */
body.admin-drawer-open {
  overflow: hidden;
}
/* ── The tables swipe stops at the table ─────────────────────────────────

/* -- The table's swipe stops at the table --------------------------------
   Measured on /admin/industries at 430px: the grid's scroll container is
   397px wide holding 815px of table, so 418px of it scrolls -- correct. But
   its overscroll-behavior-x was `auto`, which means that when the swipe
   reaches the end of the table the gesture CHAINS to the ancestor and Chrome
   rubber-bands the whole document sideways. The page slides, blank canvas
   appears beside it, and it springs back.

   That is why every static measurement said there was no overflow:
   document.scrollingElement.scrollLeft stays 0 throughout. Overscroll chaining
   is a transient rubber-band, not a scroll position, so it is invisible to
   scrollWidth and to scrollLeft and visible only to a finger.

   `contain` keeps the gesture inside the grid. The grid still scrolls to its
   full extent; what it no longer does is hand the leftover motion to the page.
   ------------------------------------------------------------------------ */
.admin-table-wrap,
.admin-card:has(> .admin-table),
.admin-card:has(> .admin-table-wrap),
.admin-scroll-x{
  overscroll-behavior-x: contain;
}

/* -- Form controls at 16px on a phone, so iOS does not zoom the page ------
   .admin-form-control was .9rem (14.4px). iOS Safari and Chrome zoom the
   whole page in when a text field under 16px receives focus. Once zoomed,
   the visual viewport is smaller than the layout viewport and the page pans
   sideways with blank canvas beside it.

   No overflow rule can stop that. overflow-x on html and body governs
   document scrolling; this is visual-viewport panning, a different thing
   entirely, which is why the page kept sliding after the document was made
   unscrollable. Measured on this site: visualViewport.scale 1.369 with a
   layout viewport of 430.

   16px is the threshold, not a preference -- 15.9px still triggers it. The
   controls are scoped to .admin-wrapper so the public site keeps its scale.
   ------------------------------------------------------------------------ */
@media (max-width: 767px){
  .admin-wrapper .admin-form-control,
  .admin-wrapper input[type="text"],
  .admin-wrapper input[type="search"],
  .admin-wrapper input[type="email"],
  .admin-wrapper input[type="password"],
  .admin-wrapper input[type="number"],
  .admin-wrapper input[type="url"],
  .admin-wrapper input[type="tel"],
  .admin-wrapper input[type="date"],
  .admin-wrapper input[type="datetime-local"],
  .admin-wrapper input[type="time"],
  .admin-wrapper input:not([type]),
  .admin-wrapper select,
  .admin-wrapper textarea{
    font-size: 16px;
  }
}

/*
 * With the duplicated title gone, .admin-page-header holds only its action
 * button. space-between with a single child pushes it left, where it reads
 * as a stray control rather than the page's action.
 */
.admin-page-header:not(:has(.admin-page-title)) { justify-content: flex-end; }


/* ============================================================================
   ADMIN — the page holds still, the grid scrolls
   ----------------------------------------------------------------------------
   html and body are overflow-x:clip (set inline by layouts/admin, after this
   file). Nothing can scroll the page sideways, so anything wider than the
   screen is simply cut off and unreachable. These rules are about making sure
   nothing needs to be.
   ========================================================================= */

/*
 * Any card containing a table scrolls it -- DESCENDANT, not child. The earlier
 * rule used :has(> .admin-table), which misses every screen that wraps its
 * table in anything at all, and those are exactly the screens still cutting
 * off their Actions column.
 */
.admin-card:has(.admin-table),
.admin-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/*
 * A grid is allowed to be wider than the phone -- that is what scrolling it is
 * for -- but it must not be wider than its own scroll container by accident,
 * so the container is pinned to the space available.
 */
.admin-card:has(.admin-table),
.admin-table-wrap { max-width: 100%; }

@media (max-width: 767px) {
  /*
   * The topbar was one of the things being cut. Four children -- hamburger,
   * back, title, View Site -- plus 40px of padding either side does not fit in
   * 430px, and with the page clipped rather than scrollable the overflow just
   * disappears off the edge, taking the hamburger with it. That is the "header
   * is not sticking" and the missing menu button in one.
   */
  .admin-topbar {
    padding: 0 14px;
    gap: 10px;
    min-width: 0;
  }

  /* The title yields first: it is the one thing already shown in the browser
     tab, and the controls either side of it are not. */
  .admin-topbar h1 {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .admin-hamburger,
  .admin-back-btn,
  .admin-topbar-link { flex: 0 0 auto; }

  /*
   * The grid keeps its shape and scrolls. The floor is per CELL rather than
   * per table, so it scales with the column count: three columns still fit a
   * phone and do not scroll, eight columns scroll. A table-level min-width
   * would force the three-column grid to scroll for nothing.
   *
   * There is deliberately no overflow-wrap here. `anywhere` participates in
   * min-content sizing, so it lets the whole table collapse to one character
   * per column -- which is how this grid came to be crushed rather than
   * scrollable. Where a genuinely unbreakable string needs handling, it is
   * break-word on that cell, which wraps without shrinking min-content.
   */
  .admin-table th,
  .admin-table td { min-width: 6.5rem; }

  /*
   * A badge, a state pill or a relationship chip is one token. It may wrap
   * onto its own line; it may not wrap inside itself. "Acti / ve" is not a
   * state, and "Gro / w furt / her" is not an outcome.
   */
  .admin-table .admin-badge,
  .admin-table .admin-state-pill,
  .admin-table [class*="chip"],
  .admin-table [class*="pill"] { white-space: nowrap; }

  /* Nothing in the content region may exceed it. */
  .admin-content > * { max-width: 100%; }
}

/* ============================================================================
   ADMIN GRID — frozen panes
   ----------------------------------------------------------------------------
   Column headings stay put while the rows move, and the first column stays put
   while the columns move, so a cell is never orphaned from what it means.
   ========================================================================= */

.admin-table-wrap,
.admin-card:has(.admin-table) {
  /*
   * The card is already a scroll container in both axes -- overflow-x:auto
   * forces overflow-y to auto too. Without a height it just grows and neither
   * axis moves, which is why a sticky header had nothing to stick to. The
   * height is what makes the viewport real.
   */
  max-height: max(22rem, calc(100vh - 15rem));
  overflow: auto;
}

/* The heading row. */
.admin-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--admin-bg);
  /*
   * inset shadow rather than border-bottom: with border-collapse:collapse the
   * collapsed border belongs to the table, so it stays behind while the cell
   * travels and the heading scrolls away from its own underline.
   */
  box-shadow: inset 0 -2px 0 var(--b-default);
}

/* The first column, so a row keeps its name when you scroll right. */
.admin-table thead th:first-child,
.admin-table tbody td:first-child {
  position: sticky;
  left: 0;
  background: var(--admin-card-bg);
}

.admin-table tbody td:first-child {
  z-index: 1;
  box-shadow: inset -1px 0 0 var(--b-subtle);
}

/* The corner belongs to both frozen panes, so it outranks each of them. */
.admin-table thead th:first-child {
  z-index: 3;
  background: var(--admin-bg);
  box-shadow: inset 0 -2px 0 var(--b-default), inset -1px 0 0 var(--b-default);
}

/*
 * A frozen cell is opaque, so it cannot inherit the row-hover tint the way a
 * transparent one does -- without this the row highlights everywhere except
 * the column you are reading it by.
 */
.admin-table tbody tr:hover td:first-child { background: var(--admin-table-row-hover); }

/* A short grid should not be pinned open to a tall empty box. */
.admin-card:has(.admin-table) > .admin-table-foot { position: sticky; bottom: 0; }

@media (max-width: 767px) {
  .admin-table-wrap,
  .admin-card:has(.admin-table) { max-height: max(18rem, calc(100vh - 11rem)); }
}

/* ============================================================================
   ADMIN — SEO editor
   ----------------------------------------------------------------------------
   The previews are the point of the screen: a title and a description are the
   two fields whose result you cannot judge from the field itself.
   ========================================================================= */

.seo-form { max-width: 780px; }

/* ------------------------------------------------------- search result --- */

.seo-serp {
  padding: var(--space-3, 24px);
  margin-bottom: var(--space-3, 24px);
  border: var(--b-hairline) solid var(--b-subtle);
  border-radius: var(--rad-2, 16px);
  background: var(--admin-card-bg);
}

.seo-serp-crumb {
  font-size: .78rem;
  color: var(--admin-text-muted);
  margin-bottom: 4px;
}

.seo-serp-crumb span { color: var(--admin-text-muted); opacity: .7; }

/*
 * Deliberately close to a real result rather than styled to match the admin:
 * the question being answered is "what will this look like there", and an
 * on-brand preview answers a different one.
 */
.seo-serp-title {
  font-size: 1.15rem;
  line-height: 1.3;
  color: #1a0dab;
  margin-bottom: 4px;
  overflow-wrap: break-word;
}

.seo-serp-desc {
  font-size: .86rem;
  line-height: 1.55;
  color: var(--admin-text-muted);
  overflow-wrap: break-word;
}

.seo-serp-desc.is-empty { font-style: italic; opacity: .75; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .seo-serp-title { color: #8ab4f8; }
}

/* -------------------------------------------------------- social card --- */

.seo-og {
  display: grid;
  grid-template-columns: 180px 1fr;
  margin-bottom: var(--space-3, 24px);
  border: var(--b-hairline) solid var(--b-subtle);
  border-radius: var(--rad-2, 16px);
  overflow: hidden;
  background: var(--admin-card-bg);
}

@media (max-width: 560px) {
  .seo-og { grid-template-columns: 1fr; }
  .seo-og-img { aspect-ratio: 1200 / 630; }
}

.seo-og-img {
  display: grid;
  place-items: center;
  min-height: 100px;
  background-color: var(--admin-bg);
  background-size: cover;
  background-position: center;
  font-size: .72rem;
  color: var(--admin-text-muted);
  border-right: var(--b-hairline) solid var(--b-subtle);
}

.seo-og-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: var(--space-2, 16px);
  min-width: 0;
}

.seo-og-host {
  font-size: .66rem;
  letter-spacing: .08em;
  color: var(--admin-text-muted);
}

.seo-og-body strong {
  font-size: .92rem;
  line-height: 1.3;
  color: var(--admin-text);
  overflow-wrap: break-word;
}

.seo-og-body > span:last-child {
  font-size: .8rem;
  line-height: 1.45;
  color: var(--admin-text-muted);
  overflow-wrap: break-word;
}

/* ------------------------------------------------------------ counters --- */

/*
 * Its own element, so it can carry a state. The previous version wrote the
 * count into the label with innerHTML on every keystroke, which rebuilds the
 * label and takes anything inside it along each time.
 */
.seo-count {
  float: right;
  font-family: var(--font-mono);
  font-size: .7rem;
  font-weight: 400;
  color: var(--admin-text-muted);
}

.seo-count.is-over { color: var(--admin-warning); font-weight: 700; }

/* Amber, not red: over the ideal length is a judgement, not an error. It still
   saves, and sometimes a longer title is the right call. */
.admin-form-control.is-over {
  border-color: var(--admin-warning-border, var(--admin-warning));
}

/* -------------------------------------------------------------- toggle --- */

.seo-toggle {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 12px;
  align-items: start;
  margin-bottom: var(--space-3, 24px);
  padding: var(--space-2, 16px);
  border: var(--b-hairline) solid var(--b-subtle);
  border-radius: var(--rad-1, 10px);
  cursor: pointer;
}

.seo-toggle input { width: 18px; height: 18px; margin-top: 2px; accent-color: var(--admin-accent); }
.seo-toggle strong { display: block; font-size: .9rem; color: var(--admin-text); }
.seo-toggle em { display: block; font-style: normal; font-size: .78rem; color: var(--admin-text-muted); }

.seo-mono { font-family: var(--font-mono); font-size: .8rem; }

/* The home headline. Previously an inline style on the element, which no
   stylesheet rule can reach -- the defect that made the admin grids
   unfixable for three attempts. */
.hero-h1 {
  font-family: var(--serif);
  font-size: clamp(1.35rem, 1.95vw, 1.75rem);
  line-height: 1.32;
  letter-spacing: -.03em;
  font-weight: 700;
  font-style: italic;
  margin-bottom: 28px;
}

.hero-h1-accent { display: inline-block; font-style: normal; }


/* ============================================================================
   Industry tiles on a dark band
   ----------------------------------------------------------------------------
   design-system-v3 only ever draws these in a light section: a white card that
   inverts to ink on hover. It says nothing about a dark ground, and the
   homepage's Industries band is dark.

   Before the surface tokens were scoped per section, this worked by accident --
   the tile painted --color-surface-raised and the text inherited --color-text,
   and BOTH were light-theme values leaking out of :root. Dark text on a light
   tile, on a dark band, by coincidence. Scoping the tokens correctly broke the
   coincidence and the tiles went dark.

   The appearance is restored deliberately: a light ground, and the text colours
   that ground owes its subtree. Switch the three declarations for the
   .dark-section .card treatment (rgba(10,10,13,.68), light border, white text)
   if the glass version reads better in place.
   ========================================================================= */

.dark-section .industry-tile {
  --color-text:        #0f0f17;
  --color-text-bright: #0f0f17;
  --color-text-muted:  rgba(15, 15, 23, .70);
  --color-text-faint:  rgba(15, 15, 23, .45);

  background: #fff;
  border-color: rgba(15, 15, 23, .10);
  color: var(--color-text);
}

/* ============================================================================
   SECTION ILLUSTRATIONS + the 60-30-10 split layout
   ----------------------------------------------------------------------------
   One source file per slot, cropped rather than scaled. The generation brief
   holds the subject inside the middle 60% of a 3:2 frame, so the crop only ever
   eats margin: 3:2 desktop, 4:3 tablet, 4:5 phone, from one file, with no
   second export and no art-directed <picture>.
   ========================================================================= */

.fig {
  margin: 0;
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--color-surface-hover);
  aspect-ratio: 4 / 5;                   /* phone first */
}

.fig img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;                     /* crop, never squash */
  object-position: center;
}

@media (min-width: 768px)  { .fig { aspect-ratio: 4 / 3; } }
@media (min-width: 1024px) { .fig { aspect-ratio: 3 / 2; } }

/* Admin preview only — never reaches a visitor. */
.fig--awaiting {
  display: grid;
  place-items: center;
  border: 2px dashed var(--color-brand-line);
  background: var(--color-brand-tint);
  text-align: center;
}

.fig--awaiting span {
  display: grid;
  gap: 4px;
  font-size: .78rem;
  line-height: 1.5;
  color: var(--color-text-muted);
  padding: var(--space-md);
}

.fig--awaiting strong { color: var(--color-text-bright); font-size: .9rem; }
.fig--awaiting code { font-family: var(--font-mono); font-size: .72rem; }

/* ------------------------------------------------------ the split layout ---

   60-30-10 as composition rather than colour: one side carries roughly 60% of
   the section's weight, the other 30%, and the remainder is space that is part
   of the arithmetic rather than what is left over once the boxes are placed.

   The sides alternate down the page. Every section 60% image reads as a
   catalogue; every section 60% text is the wall of words this page already had.
   The alternation is what stops either.
   --------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-xl);
  align-items: center;
}

@media (min-width: 900px) {
  /* image leads */
  .split                { grid-template-columns: 1.6fr 1fr; }
  /* text leads — same markup order, reversed visually so the DOM stays
     readable in source order and for a screen reader */
  .split--text-led      { grid-template-columns: 1fr 1.6fr; }
  .split--text-led .fig { order: 2; }
}

.split-copy { max-width: 34rem; }

/*
 * The measure. design-system-v3 sets .prose at 65ch; a section lead is shorter
 * than prose and wants a tighter cap, or it runs the full column width and
 * becomes the thing it was meant to break up.
 */
.split-copy .section-lead,
.split-copy .body { max-width: 46rem; }

.split-copy > * + * { margin-top: var(--space-md); }
.split-copy .cta-actions { margin-top: var(--space-lg); }

/* The hero lead. Was an inline style; the class is reachable and the measure
   is the point -- a lead that runs the full column stops being a lead. */
.hero-lead {
  font-size: 1.06rem;
  line-height: 1.7;
  font-weight: 350;
  color: var(--color-text-dim);
  max-width: 34rem;
  margin: 0 auto var(--space-2xl);
}


/* ── Problem cards: an accent edge, and panels that end together ──────────
   The subgrid lines the panels up at the TOP. Their bottoms were ragged,
   because a filled box at align-self:start keeps its content height while the
   row is sized to the tallest sibling. Stretching the box -- not the row --
   makes the bottom edges agree without touching the rhythm above it.
   ────────────────────────────────────────────────────────────────────────── */
.problem-card {
  border-left: 3px solid color-mix(in srgb, var(--color-brand, #F98603) 55%, transparent);
  transition: border-left-color .22s ease;
}
.problem-card:hover { border-left-color: var(--color-brand, #F98603) }

.problem-card > .outcome-block {
  align-self: stretch;
  display: flex;
  flex-direction: column;
}
/* The tag keeps its own height; the text takes the slack, so a short outcome
   sits at the top of a taller box rather than being centred in it. */
.problem-card > .outcome-block > .outcome-text { flex: 1 }

/* ── Hero coins: extruded, not painted ────────────────────────────────────
   Neumorphism only works when the shape and its ground are the same colour --
   the illusion is a disc pushed OUT of the hero, not a coloured token laid on
   top of it. So the brand gradient these carried is gone, which also returns a
   large slice of the accent budget: seven saturated discs were competing with
   the very product logos the hero exists to show.

   On a #08080c ground there is almost no room to go lighter, so the light half
   of the shadow pair stays faint and the dark half plus the inset top rim do
   the work. Overdriving the highlight is what makes dark neumorphism look
   cheap.

   The rim is the only accent left: 1.5px, coming up to full brand on hover.
   ────────────────────────────────────────────────────────────────────────── */
.orbit-icon {
  /* Translucent, not a colour. The coins orbit and the backdrop's orange
     blob drifts, so there is no fixed ground colour to match -- this
     lightens whatever is behind it and stays in step everywhere. */
  background: linear-gradient(152deg, rgba(255,255,255,.085) 0%, rgba(255,255,255,.022) 46%, rgba(0,0,0,.22) 100%);
  border: 1.5px solid color-mix(in srgb, var(--color-brand, #F98603) 38%, transparent);
  box-shadow:
    8px 10px 22px rgba(14, 8, 3, .70),
    -6px -6px 16px rgba(255, 236, 214, .055),
    inset 0 1.5px 0 rgba(255, 245, 232, .13),
    inset 0 -8px 15px rgba(0, 0, 0, .42);
  transition: border-color .25s ease, box-shadow .25s ease;
}

.orbit-icon:hover {
  border-color: var(--color-brand, #F98603);
  box-shadow:
    9px 12px 26px rgba(0, 0, 0, .70),
    -7px -7px 18px rgba(255, 255, 255, .06),
    inset 0 1px 0 rgba(255, 255, 255, .09),
    0 0 0 4px color-mix(in srgb, var(--color-brand, #F98603) 12%, transparent);
}

/* A circle inside a circle, so the ring is the same thickness the whole way
   round -- the old rounded square left it fat at the corners and thin at the
   edges, which is what read as a clumsy border.

   80% and 11px of padding are arithmetic, not taste: a circle of diameter d
   holds a square of at most d/sqrt(2), so 70.4 - 22 = 48.4 <= 49.8 keeps the
   corners of ANY aspect ratio inside the plate. These are six different marks
   and only one of them needs to be square for a guess to be wrong. */
.orbit-logo {
  width: 80%; height: 80%;
  border-radius: 50%;
  padding: 11px;
  /* Stays pure #fff on purpose. These logo PNGs carry a white background,
     and object-fit:contain draws that rectangle inside the plate -- against
     an off-white plate it showed as a hard square seam in a round face.
     Same white, no seam. The warmth lives in the inset shadow instead,
     which lands in the padding ring and not under the artwork. */
  background: #fff;
  /* The inset lands in the 11px padding ring and seats the plate INTO the
     coin. Inset shadows paint above the background and below replaced
     content, so the logo artwork is not touched. */
  box-shadow:
    inset 0 2px 5px rgba(24, 14, 6, .17),
    inset 0 -1px 0 rgba(255, 255, 255, .9),
    0 2px 8px rgba(0, 0, 0, .45);
}

/* The coins already stop moving here; the rim should stop reacting too. */
@media (prefers-reduced-motion: reduce) {
  .orbit-icon { transition: none }
}

/* ── Light cards in dark sections: widen the contract ─────────────────────
   .dark-section .surface-card already hands its subtree light-card text
   tokens. The trouble was that the contract named ONE component while several
   others also paint themselves #fff -- so a card could be white while every
   descendant was still being coloured for the dark band behind it.

   That is how "Get Found" ended up at --c-gray-300 (#d1d5db) on white: about
   1.5:1, against the 4.5:1 AA needs for text that size. The pill went too,
   since rgba(255,255,255,.08) over white is just white.

   The set was found by sweeping eleven live pages for white cards sitting in
   dark bands, not by guessing. .card is deliberately absent: .dark-section
   .card repaints it dark, so it is a dark card and its light-on-dark text is
   already right.
   ────────────────────────────────────────────────────────────────────────── */
.dark-section :is(.post-card, .image-card, .team-card, .cmq-item, .hstrip-item) {
  --color-text:        var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  --color-text-faint:  rgba(15, 15, 23, .45);
  --color-border:      rgba(15, 15, 23, .12);
}

.dark-section :is(.post-card, .image-card, .team-card) {
  color: var(--color-text);
}

/* The category label. Same values .light-section .tag uses, because the card
   IS a light section as far as anything inside it is concerned:
   #374151 on #f3f4f6 measures 9.7:1. */
.dark-section :is(.post-card, .image-card, .team-card) .tag {
  background: var(--c-gray-100);
  color: var(--c-gray-700);
  border: none;
}

/* Byline and date. --c-gray-400 was 2.54:1 on white; --c-gray-600 (#4b5563) is
   7.56:1. This mirrors the .light-section rule that already exists for exactly
   these three classes. */
.dark-section :is(.post-card, .image-card, .team-card) :is(.post-meta, .image-card-meta, .team-role) {
  color: var(--c-gray-600);
}

/* ── Footer social icons: bigger, and centred whatever the display ────────
   The base rule centred with display:grid + place-items:center, and the mobile
   block overrides display with inline-flex !important. place-items is
   align-items + JUSTIFY-ITEMS, and justify-items is a grid property that flex
   ignores -- so the override silently dropped the horizontal centring and left
   the glyph against the left edge. Vertically it stayed centred, which is why
   it read as slightly off rather than plainly broken.

   justify-content is honoured by BOTH grid and flex, so the centring now
   survives whichever display wins. Fixing the cause rather than out-shouting
   the !important, which would leave the same trap for the next override.

   44px is the minimum touch target WCAG 2.5.8 and the Apple HIG both ask for,
   and these are the only tap targets down here. The 16x16 on the <svg> tags
   stays: those are presentation attributes and CSS outranks them, so one rule
   beats editing four inline SVGs and keeps the size in one place.
   ────────────────────────────────────────────────────────────────────────── */
.foot-social { gap: 12px }

.foot-social a {
  width: 44px;
  height: 44px;
  justify-content: center;
  align-items: center;
}

.foot-social a svg {
  width: 20px;
  height: 20px;
  display: block;
  flex-shrink: 0;
}

/* ── Currency ─────────────────────────────────────────────────────────────
   Both prices are in the HTML on every page, because a page cache sits in
   front of this app and the markup has to be identical for every visitor. One
   is revealed here. The default below is INR so that a visitor with JS off, or
   before the head script runs, sees the primary market's price rather than
   nothing.
   ────────────────────────────────────────────────────────────────────────── */
[data-cur] { display: none }
:root[data-currency="INR"] [data-cur="INR"],
:root[data-currency="USD"] [data-cur="USD"],
:root:not([data-currency]) [data-cur="INR"] { display: inline }

.cur-toggle {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  border-radius: var(--radius-button, 8px);
  border: 1px solid var(--color-border);
  background: var(--color-surface-interactive);
}

.cur-toggle-btn {
  font: inherit;
  font-size: .78rem;
  font-weight: 600;
  line-height: 1;
  padding: 7px 13px;
  border: none;
  border-radius: calc(var(--radius-button, 8px) - 3px);
  background: none;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background .18s ease, color .18s ease;
}

.cur-toggle-btn:hover { color: var(--color-text) }

.cur-toggle-btn[aria-pressed="true"] {
  background: var(--color-brand);
  color: #fff;
}

/* The toggle is a control, so it needs a visible focus ring of its own -- the
   pressed state is a colour change and colour alone is not a focus indicator. */
.cur-toggle-btn:focus-visible {
  outline: 2px solid var(--color-brand);
  outline-offset: 2px;
}

/* ── Landing page illustration slot ───────────────────────────────────────
   Capped and centred between the section head and the content below it. Full
   bleed would make the picture the section rather than an illustration of it,
   and these bands carry an argument in words that the image only supports.
   Renders nothing at all until the file exists -- see x-figure.
   ────────────────────────────────────────────────────────────────────────── */
.ug-fig {
  max-width: min(560px, 100%);
  margin: 0 auto var(--space-5);
}

/* ── 1. The light-card contract, keyed on .on-light ───────────────────────
   .on-light is this codebase's own marker for "this card paints itself light
   inside a dark band" -- the consultation form's comment calls it load-bearing.
   The contract belongs on THAT, not on a list of component class names, which
   is how the form's labels ended up at 1.47:1 after I widened the contract to
   .post-card, .image-card and .team-card and stopped there.
   ────────────────────────────────────────────────────────────────────────── */
.dark-section .on-light {
  --color-text:        var(--c-gray-900);
  --color-text-bright: var(--c-gray-900);
  --color-text-dim:    rgba(15, 15, 23, .85);
  --color-text-muted:  rgba(15, 15, 23, .65);
  --color-text-subtle: rgba(15, 15, 23, .55);
  --color-text-faint:  rgba(15, 15, 23, .45);
  --color-border:      rgba(15, 15, 23, .12);
  color: var(--color-text);
}

/* #4b5563 on white is 7.56:1; the uppercase 13px label was 1.47:1. */
.dark-section .on-light :is(.field-label, .ug-field-label) { color: var(--c-gray-700) }
.dark-section .on-light .field-hint,
.dark-section .on-light .form-note                          { color: var(--c-gray-600) }


/* ── 2. "Most popular" gets its own space ─────────────────────────────────
   It was absolutely positioned over the card head and covered the tier name
   outright. Centred on the top edge it reads as a ribbon, and the head is
   padded so the name clears it instead of competing for the same pixels.
   ────────────────────────────────────────────────────────────────────────── */
.ug-pkg.is-featured .ug-pkg-flag {
  top: 0;
  right: auto;
  left: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
}

.ug-pkg.is-featured .ug-pkg-head {
  padding-top: calc(var(--space-md) + 0.7rem);
}


/* ── 3. The workflow chain stays one line ─────────────────────────────────
   Eight steps wrapping to a second row stops the sequence reading as one
   flow -- "Grow" ends up looking like a separate item rather than the end of
   the process. It scrolls at narrow widths instead of fragmenting, because a
   broken sequence is worse than one the reader has to nudge.
   ────────────────────────────────────────────────────────────────────────── */
.ug-chain--flow {
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 4px;
  /* Fades at the edge so a scrollable row looks scrollable. */
  mask-image: linear-gradient(to right, #000 0, #000 94%, transparent 100%);
}
.ug-chain--flow::-webkit-scrollbar { display: none }
.ug-chain--flow li {
  flex: none;
  padding: 0.32rem 0.62rem;
  font-size: 0.78rem;
}
.ug-chain--flow li:not(:last-child)::after { margin-left: 0 }



/* ── 4. "Real results": air, and one accent ───────────────────────────────
   Five identical neutral boxes then two neutral cards, every one at
   --space-md, no accent anywhere. The section reads grey. The accent goes on
   the metric key only -- one orange thing per card, not an orange section.
   ────────────────────────────────────────────────────────────────────────── */
#results .ug-strip { gap: var(--space-lg); margin-top: var(--space-xl) }
#results .ug-strip li { padding: var(--space-lg) }
#results .ug-strip-k { font-size: 1.02rem; margin-bottom: 0.15rem }
#results .ug-strip-v { line-height: 1.6 }

#results .ug-cases { gap: var(--space-xl); margin-top: var(--space-2xl, 4rem) }
#results .ug-case  { padding: var(--space-xl) }
#results .ug-case-k { color: var(--color-brand) }
#results .ug-case-title { margin: var(--space-sm) 0 var(--space-lg); line-height: 1.35 }

#results .ug-metrics { gap: var(--space-lg) }
#results .ug-metric  { padding: var(--space-lg) }
#results .ug-metric-k { color: var(--color-brand) }

/* ── "What you have already tried": a timed accordion ─────────────────────
   Three cards side by side put twelve bullet points on screen at once with
   nothing directing the eye. One panel at a time shows four.
   ────────────────────────────────────────────────────────────────────────── */
.ug-acc {
  margin-top: var(--space-xl);
  border-top: 1px solid var(--color-border);
}

.ug-acc-item {
  position: relative;
  border-bottom: 1px solid var(--color-border);
}

.ug-acc-h { margin: 0; font: inherit }

.ug-acc-head {
  width: 100%;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-2);
  background: none;
  border: 0;
  font: inherit;
  color: var(--color-text);
  text-align: left;
  cursor: pointer;
  transition: color .2s ease;
}
.ug-acc-head:hover { color: var(--color-brand) }
.ug-acc-head:focus-visible { outline: 2px solid var(--color-brand); outline-offset: -3px }

.ug-acc-n {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .1em;
  color: var(--color-text-faint);
}
.ug-acc-item.is-open .ug-acc-n { color: var(--color-brand) }

.ug-acc-title {
  font-family: var(--font-heading);
  font-size: 1.12rem;
  font-weight: 700;
  line-height: 1.3;
}

.ug-acc-chev { transition: transform .28s cubic-bezier(.22,1,.36,1); flex: none }
.ug-acc-item.is-open .ug-acc-chev { transform: rotate(180deg) }

.ug-acc-panel { padding: 0 var(--space-2) var(--space-5) }

.ug-acc-inner {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: var(--space-5);
  align-items: center;
}

.ug-acc-copy { min-width: 0 }

.ug-acc-fig {
  margin: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #fff;
}
.ug-acc-fig img { display: block; width: 100%; height: auto }

.ug-acc-fig--none {
  display: grid;
  place-items: center;
  aspect-ratio: 3 / 2;
  background: var(--color-surface-hover);
  color: var(--color-text-faint);
}

/* The panel fades in rather than appearing. Reduced motion gets neither. */
@media (prefers-reduced-motion: no-preference) {
  .ug-acc-item.is-open .ug-acc-panel { animation: ugAccIn .34s cubic-bezier(.22,1,.36,1) both }
  @keyframes ugAccIn { from { opacity: 0; transform: translateY(6px) } to { opacity: 1; transform: none } }
}

@media (max-width: 860px) {
  .ug-acc-inner { grid-template-columns: 1fr; gap: var(--space-4) }
  /* The picture leads on a phone: it is what makes the panel worth opening. */
  .ug-acc-fig, .ug-acc-fig--none { order: -1 }
  .ug-acc-title { font-size: 1rem }
}

/* ── "Why choose Prabhabi": one pillar at a time ──────────────────────────
   A white card on a dark band, because the artwork behind the text is
   white-ground. .on-light (see the light-card contract) gives the subtree its
   dark text tokens.
   ────────────────────────────────────────────────────────────────────────── */
.ug-pillar-stage {
  position: relative;
  background: #fff;
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: 0 18px 50px rgba(0, 0, 0, .38);
}

/* Pinned height. Four texts of different lengths in one box means the tallest
   sets it, or the section jumps under the reader every few seconds. */
.ug-pillar-slides {
  position: relative;
  min-height: 21rem;
}

.ug-pillar-slide {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-5);

  /* The scrim runs left to right rather than flat across. The artwork keeps its
     shapes bottom-right and its centre empty, so a uniform wash would fade the
     only part worth showing. Opaque where the words are, clear where the
     picture is. */
  background-image:
    linear-gradient(100deg, #fff 0%, rgba(255,255,255,.90) 44%, rgba(255,255,255,.30) 74%, rgba(255,255,255,0) 100%),
    var(--pillar-bg, none);
  background-size: cover, cover;
  background-position: center, center right;
  background-repeat: no-repeat, no-repeat;

  opacity: 0;
  visibility: hidden;
  transition: opacity .55s ease, visibility .55s;
}

.ug-pillar-slide.is-on { opacity: 1; visibility: visible }

.ug-pillar-stage .ug-pillar-icon {
  display: grid;
  place-items: center;
  width: 46px; height: 46px;
  border-radius: var(--radius-md);
  background: var(--color-brand-tint);
  color: var(--color-brand-text, var(--color-brand));
}

.ug-pillar-slide strong {
  font-family: var(--font-heading);
  font-size: 1.32rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--color-text);
}

.ug-pillar-slide em {
  font-style: normal;
  font-size: .96rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  max-width: 22rem;
}

.ug-pillar-dots {
  position: relative;
  display: flex;
  gap: 6px;
  padding: 0 var(--space-5) var(--space-5);
}

/* 8px of paint inside a 44px target -- small to look at, big enough to hit. */
.ug-pillar-dot {
  width: 44px; height: 44px;
  padding: 0; border: 0; background: none; cursor: pointer;
  display: grid; place-items: center;
}
.ug-pillar-dot span {
  display: block;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: rgba(15, 15, 23, .22);
  transition: background .2s ease, transform .2s ease;
}
.ug-pillar-dot:hover span { background: rgba(15, 15, 23, .42) }
.ug-pillar-dot.is-on span { background: var(--color-brand); transform: scale(1.35) }
.ug-pillar-dot:focus-visible { outline: 2px solid var(--color-brand); outline-offset: -8px; border-radius: 50% }

@media (prefers-reduced-motion: reduce) {
  .ug-pillar-slide { transition: none }
}

@media (max-width: 860px) {
  .ug-pillar-slides { min-height: 19rem }
  .ug-pillar-slide {
    padding: var(--space-5) var(--space-4);
    /* Less width means less room for the picture to sit clear of the words, so
       the scrim covers more of it rather than letting them collide. */
    background-image:
      linear-gradient(100deg, #fff 0%, rgba(255,255,255,.94) 58%, rgba(255,255,255,.55) 88%, rgba(255,255,255,.3) 100%),
      var(--pillar-bg, none);
  }
  .ug-pillar-slide strong { font-size: 1.16rem }
}

/* ── "Facing these challenges?": one big panel at a time ──────────────────
   Same carousel as before -- only the track width changes, so all the existing
   prev/next/dot/keyboard machinery keeps working. One slide per view also
   means the controls stop being hidden: there is finally something to steer.
   ────────────────────────────────────────────────────────────────────────── */
#problem .ug-rail-track { grid-auto-columns: 100% }

#problem .ug-prob {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: var(--space-6);
  padding: var(--space-6);
  /* A share of the screen rather than a fixed height, so it scales with the
     display. Clamped: it cannot collapse on a short laptop window, and cannot
     run past the fold on a tall monitor. */
  min-height: clamp(24rem, 62vh, 34rem);
}

#problem .ug-prob-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  min-width: 0;
}

#problem .ug-prob-n {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--color-brand);
}

#problem .ug-prob-title {
  font-size: clamp(1.4rem, 2.4vw, 2rem);
  line-height: 1.18;
  margin: 0;
}

#problem .ug-prob-body {
  font-size: 1.02rem;
  line-height: 1.7;
  max-width: 30rem;
  color: var(--color-text-muted);
}

#problem .ug-prob-fig {
  margin: 0;
  min-width: 0;
  border-radius: var(--radius-lg);
  overflow: hidden;
}
#problem .ug-prob-fig img { display: block; width: 100%; height: auto }

/* The controls matter now that they do something, so they get room. */
#problem .ug-rail-controls { margin-top: var(--space-5) }

@media (max-width: 900px) {
  #problem .ug-prob {
    grid-template-columns: 1fr;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-4);
    min-height: 0;
  }
  /* The picture leads. It is the reason the panel is worth looking at, and text
     first on a phone would be the wall of words this replaces. */
  #problem .ug-prob-fig { order: -1 }
  #problem .ug-prob-body { font-size: .96rem }
}

/* ── Accordion motion: everything moves up ────────────────────────────────
   One motion language rather than four effects. The illustration drifts
   upward across the panel's dwell, the content rose in from below, and the
   collapse pulls the next item up into the space — so the column reads as one
   thing being drawn upward and handed on.
   ────────────────────────────────────────────────────────────────────────── */

/* The panel's height is driven from JS, measured rather than guessed. */
.ug-acc-panel {
  overflow: hidden;
  transition: height .38s cubic-bezier(.22, 1, .36, 1);
}

/* The old fade-in is replaced by the per-element rise below; leaving both on
   would double up on the same elements. */
@media (prefers-reduced-motion: no-preference) {
  .ug-acc-item.is-open .ug-acc-panel { animation: none }
}

/* ── The step number and its countdown ring ── */
.ug-acc-n {
  position: relative;
  display: grid;
  place-items: center;
  width: 40px; height: 40px;
  flex: none;
}
.ug-acc-n > i {
  font-style: normal;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--color-text-faint);
  transition: color .3s ease;
}
.ug-acc-item.is-open .ug-acc-n > i { color: var(--color-brand) }

.ug-acc-ring {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  transform: rotate(-90deg);       /* start the sweep at twelve o'clock */
  pointer-events: none;
}
.ug-acc-ring circle {
  fill: none;
  stroke: var(--color-brand);
  stroke-width: 2;
  stroke-linecap: round;
  /* 2 * pi * 16 = 100.53. Fully offset means fully undrawn. */
  stroke-dasharray: 100.53;
  stroke-dashoffset: 100.53;
}
/* A faint track, so the ring reads as a countdown rather than an arc. */
.ug-acc-n::before {
  content: '';
  position: absolute;
  inset: 2px;
  border-radius: 50%;
  border: 2px solid var(--color-border);
  opacity: 0;
  transition: opacity .3s ease;
}
.ug-acc-item.is-open .ug-acc-n::before { opacity: 1 }

/* ── Content rises in, in reading order ── */
@media (prefers-reduced-motion: no-preference) {
  .ug-acc-item.is-open .ug-misses li,
  .ug-acc-item.is-open .ug-tried-line,
  .ug-acc-item.is-open .ug-acc-fig {
    animation: ugRise .44s cubic-bezier(.22, 1, .36, 1) both;
  }
  .ug-acc-item.is-open .ug-acc-fig        { animation-delay: .02s }
  .ug-acc-item.is-open .ug-misses li:nth-child(1) { animation-delay: .08s }
  .ug-acc-item.is-open .ug-misses li:nth-child(2) { animation-delay: .14s }
  .ug-acc-item.is-open .ug-misses li:nth-child(3) { animation-delay: .20s }
  .ug-acc-item.is-open .ug-misses li:nth-child(4) { animation-delay: .26s }
  .ug-acc-item.is-open .ug-misses li:nth-child(5) { animation-delay: .32s }
  .ug-acc-item.is-open .ug-tried-line     { animation-delay: .34s }

  @keyframes ugRise {
    from { opacity: 0; transform: translateY(16px) }
    to   { opacity: 1; transform: none }
  }
}

/* ── The illustration drifts upward across the dwell ──
   The transform duration is set from JS, because it has to match this panel's
   own reading time rather than a constant. */
.ug-acc-fig img { will-change: transform }

/* The closing panel's copy leaves upward too, so the handover reads as one
   continuous pull rather than a disappearance and an appearance. */
.ug-acc-item:not(.is-open) .ug-acc-inner {
  opacity: 0;
  transform: translateY(-10px);
  transition: opacity .22s ease, transform .3s cubic-bezier(.22, 1, .36, 1);
}
.ug-acc-item.is-open .ug-acc-inner {
  opacity: 1;
  transform: none;
  transition: opacity .3s ease .06s, transform .38s cubic-bezier(.22, 1, .36, 1);
}

@media (prefers-reduced-motion: reduce) {
  .ug-acc-panel { transition: none }
  .ug-acc-item .ug-acc-inner { transition: none; opacity: 1; transform: none }
  .ug-acc-ring circle { display: none }
  .ug-acc-fig img { transform: none !important }
}

/* ── The why grid must be allowed to shrink ───────────────────────────────
   A grid item defaults to min-width:auto, so it refuses to go narrower than
   its content's min-content width. The workflow chain is a nowrap flex row,
   whose min-content width is the whole row -- 694px -- so its column demanded
   694px at every viewport. Below 900px that overflowed .ug-why by up to 441px;
   above it, the two-column grid gave the chain its 694px and squeezed the
   pillar card down to 49px at 900px and 117px at 1024px.

   overflow-x:auto on the chain could not save it: a scroll container only
   scrolls once something forces it narrower, and nothing was.

   Same defect as the admin x-scroll hunt, somewhere new: an item does not
   shrink below its content unless it is told it may.
   ────────────────────────────────────────────────────────────────────────── */
.ug-why > * { min-width: 0 }

/* The chain scrolls at every width. It lives in a half-width column, so there
   is no viewport at which all eight steps fit unscrolled. */
.ug-chain--flow { max-width: 100% }

/* Full width now, so it needs its own top margin rather than the one it
   inherited from sitting under a paragraph. */
#why .ug-chain--flow { margin-top: var(--space-6); width: 100% }

/* ── One focus ring, not three ────────────────────────────────────────────
   A focused .field was picking up its own border-colour change, its own 3px
   tinted box-shadow AND the global :focus-visible outline -- three rings in
   one colour at three radii. On the 47px select that reads as a doubled orange
   edge crowding the option list.

   The global outline is suppressed only for controls that already style their
   own focus, and that ring is strengthened to carry the job alone: a 10% tint
   was decorative, 45% is an indicator.
   ────────────────────────────────────────────────────────────────────────── */
.field:focus,
.field:focus-visible,
.field-input:focus,
.field-input:focus-visible,
select.field:focus,
select.field:focus-visible {
  outline: none;
  border-color: var(--color-brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand, #F98603) 45%, transparent);
  /* No lift. The OS anchors a select's popup to the control, so moving the
     control on focus moves the list with it. */
  transform: none;
}

/* Forced-colours mode ignores box-shadow, so the outline has to come back or
   there is no focus indicator at all there. */
@media (forced-colors: active) {
  .field:focus-visible,
  .field-input:focus-visible,
  select.field:focus-visible { outline: 3px solid Highlight; outline-offset: 2px }
}


/* ── The eyebrow pill's optical centring ──────────────────────────────────
   Tracking was 1px here and 2.5px on the plain .eyebrow, so the same element
   was spaced two different ways on one page. Matched to 2.5px.

   Letter-spacing is added after EVERY character, the last one included, so a
   pill with symmetric padding always reads right-heavy. Taking the tracking
   back off the right edge is what makes it look centred.
   ────────────────────────────────────────────────────────────────────────── */
.section-eyebrow {
  letter-spacing: 2.5px;
  padding-left: 18px;
  padding-right: calc(18px - 2.5px);
}

/* ── The workflow chain drifts on phones ──────────────────────────────────
   Eight steps do not fit a 390px screen, and a row the reader has to drag is
   one most readers never finish -- so "Grow" went unseen by anybody who did
   not think to swipe. Drifting delivers the whole sequence unprompted, which
   is the entire point of showing it as one flow.

   Runs on cmq-drift, the same keyframes the customer and hero strips use: a
   track of two identical runs translated 0 to -50%, seamless because the
   second run starts exactly where the first did.
   ────────────────────────────────────────────────────────────────────────── */
.ug-chain-viewport {
  overflow: hidden;
  margin-top: var(--space-6);
  /* Fading the edges is what makes a moving row read as continuing rather
     than as being cut off. */
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}

.ug-chain-track {
  display: flex;
  width: max-content;
  animation: cmq-drift calc(var(--chain-steps, 8) * 2.4s) linear infinite;
  will-change: transform;
}

/* Stops for anybody actually looking at it, or tabbing through it. */
.ug-chain-viewport:hover .ug-chain-track,
.ug-chain-track:focus-within { animation-play-state: paused }

/* Inside the track the list is a plain row again: the viewport owns the
   clipping and the fade now. */
.ug-chain-viewport .ug-chain--flow {
  overflow: visible;
  mask-image: none;
  width: auto;
  max-width: none;
  margin: 0;
  padding-right: var(--space-xs);
}

@media (min-width: 900px) {
  /* All eight fit here -- measured -- so nothing moves. Motion on a screen
     with room for the content is noise. */
  .ug-chain-viewport { overflow: visible; mask-image: none }
  .ug-chain-track { animation: none; width: 100% }
  .ug-chain-track .ug-chain + .ug-chain { display: none }
}

@media (prefers-reduced-motion: reduce) {
  /* Back to the row it was: scrollable, and costing the reader nothing. */
  .ug-chain-track { animation: none; width: 100% }
  .ug-chain-track .ug-chain + .ug-chain { display: none }
  .ug-chain-viewport { overflow-x: auto; scrollbar-width: none }
  .ug-chain-viewport::-webkit-scrollbar { display: none }
}

/* Best-effort: some platforms honour font-size on <option> and size the popup
   accordingly, others ignore it. Harmless where ignored. The real control over
   that popup's width is how long the option text is. */
select.field option { font-size: 14px }

/* ── /careers with nothing open ───────────────────────────────────────────
   The offer, not the apology. Two "check back soon" cards used to occupy this
   space and send a willing candidate away.
   ────────────────────────────────────────────────────────────────────────── */
.jobs-none {
  display: grid;
  grid-template-columns: 1.35fr 1fr;
  gap: var(--space-6);
  align-items: center;
}

.jobs-none-copy { min-width: 0 }

.jobs-none-list {
  list-style: none;
  margin: var(--space-5) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.jobs-none-list li {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: .95rem;
  line-height: 1.6;
  color: var(--color-text-muted);
}
.jobs-none-list svg { color: var(--color-brand); flex: none; margin-top: 4px }
.jobs-none-list strong { color: var(--color-text); font-weight: 600 }

.jobs-none-cta { margin-top: var(--space-5) }

.jobs-none-fine {
  margin-top: var(--space-3);
  font-size: .82rem;
  line-height: 1.6;
  color: var(--color-text-faint);
  max-width: 34rem;
}

/* Decorative, and aria-hidden in the markup: it repeats the copy beside it. */
.jobs-none-aside {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  border-radius: var(--radius-xl);
  border: 1px solid var(--color-border);
  background: var(--color-surface-raised);
}
.jobs-none-mark {
  display: grid;
  place-items: center;
  width: 62px; height: 62px;
  border-radius: var(--radius-lg);
  background: var(--color-brand-tint);
  color: var(--color-brand);
}
.jobs-none-aside p {
  margin: 0;
  font-family: var(--font-heading);
  font-size: 1.05rem;
  line-height: 1.4;
  color: var(--color-text);
}

@media (max-width: 900px) {
  .jobs-none { grid-template-columns: 1fr; gap: var(--space-4) }
  /* The aside only repeats the copy, so on a phone it goes last rather than
     pushing the actual invitation down the screen. */
  .jobs-none-aside { order: 2 }
}

/* ── The application form on a dark ground ────────────────────────────────
   Nothing set a placeholder colour, so every field fell back to the browser
   default: a mid grey picked for white backgrounds, sitting on a field that is
   nearly black. "https://linkedin.com/in/..." was all but invisible.

   .58 clears AA against the field's own background while staying clearly
   dimmer than a typed value -- a placeholder that reads as strongly as real
   input is its own bug. opacity:1 because Firefox dims placeholders again on
   top of whatever colour it is handed.
   ────────────────────────────────────────────────────────────────────────── */
.apply-form input::placeholder,
.apply-form textarea::placeholder {
  color: rgba(255, 255, 255, .58);
  opacity: 1;
}

/* The file control was the raw OS button: a white block, and the only one on
   the page. Styling the button rather than replacing the input keeps the
   keyboard and screen-reader behaviour a native file input gives for free. */
.apply-form input[type="file"] {
  padding: 9px 10px;
  cursor: pointer;
}

.apply-form input[type="file"]::file-selector-button {
  margin-right: 12px;
  padding: 7px 14px;
  border: 1px solid rgba(255, 255, 255, .22);
  border-radius: 8px;
  background: rgba(255, 255, 255, .08);
  color: #fff;
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .18s ease, border-color .18s ease;
}
.apply-form input[type="file"]::file-selector-button:hover {
  background: var(--color-brand-tint);
  border-color: var(--color-brand);
  color: var(--color-brand);
}

/* "No file chosen" is the browser's own text and cannot be styled directly;
   it inherits the input's colour, which is why the input is explicitly white
   rather than left to the UA. */
.apply-form input[type="file"] { color: rgba(255, 255, 255, .78) }

/* ── <mark>, defensively ──────────────────────────────────────────────────
   A <mark> that survives an edit in the admin must not arrive as the browser's
   yellow highlighter, which is what happened on /about after mark.kw's styling
   was removed with the treatment it belonged to.

   The site's real emphasis is .focus-phrase -- one algorithmically chosen
   phrase per block. This is only a floor, so a highlight typed into the CMS
   degrades to something restrained instead of shouting.
   ────────────────────────────────────────────────────────────────────────── */
mark, mark.kw {
  /* Emphasis on this site is an underline, never a block of colour behind
     the words -- see .focus-phrase. A highlighter here would be a second
     emphasis language, which is what the migration to .focus-phrase
     removed in the first place. */
  background: none;
  box-shadow: inset 0 -.14em 0 color-mix(in srgb, var(--color-brand, #F98603) 55%, transparent);
  color: inherit;
  font-weight: 500;
  /* Vertical only. Horizontal padding pushed the following punctuation
     away from the word and left a gap before the full stop. */
  padding: .08em .04em;
  border-radius: 3px;
  /* A phrase that wraps becomes two boxes; without this the shape is cut
     square at the break and reads as broken rather than continued. */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  /* No text-shadow or glow: this sits in body copy and has to stay readable. */
}

/* ── Contact form placeholders ────────────────────────────────────────────
   The white panel left placeholders to the browser, which put --c-gray-400
   (#9ca3af) on white: 2.54:1, against the 4.5:1 AA needs. #6c6a78 measures
   5.29:1 and stays clearly lighter than a typed value, which is the whole
   distinction a placeholder has to hold on to.

   Set for the panel rather than per field, so a field added later cannot
   quietly inherit the browser's choice again.
   ────────────────────────────────────────────────────────────────────────── */
.contact-form-panel input::placeholder,
.contact-form-panel textarea::placeholder {
  color: var(--c-gray-500, #6c6a78);
  opacity: 1;
}

/* ── Two bands of the same tone in a row ──────────────────────────────────
   Sometimes unavoidable: a page whose first and last bands are both pinned
   dark can only alternate strictly when it has an odd number of them. /about
   has six.

   The complaint was never the repeat itself, it was that two dark bands read
   as ONE long block. A hairline restores the boundary, so the sections stay
   countable without breaking the pinning that makes every page close dark.

   One rule covers every instance on the site -- two on the home page, one each
   on /services, /solutions and /unlock-growth.
   ────────────────────────────────────────────────────────────────────────── */
.dark-section + .dark-section,
/* A script, style or template between two bands renders nothing, so it
   should not hide the join. The sibling combinator counts it regardless. */
.dark-section + script + .dark-section,
.dark-section + style + .dark-section,
.dark-section + template + .dark-section { border-top: 1px solid rgba(255, 255, 255, .07) }

.light-section + .light-section,
.light-section + script + .light-section,
.light-section + style + .light-section,
.light-section + template + .light-section { border-top: 1px solid rgba(15, 15, 23, .07) }

/* A product with no logo shows its initial rather than the default spark that
   three of them shared. Same idea as the team card's avatar fallback: name the
   thing, so an absent asset does not look like a broken one. */
.product-initial {
  font-family: var(--font-heading);
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1;
  color: var(--color-brand);
}

/* .problem-tag is now an h2 on the service pages -- it labels the band and had
   to be reachable by heading navigation. It already sets its own size, weight,
   tracking and case, so only the browser's heading margins need cancelling for
   it to render exactly as the span did. */
h2.problem-tag { margin: 0; line-height: 1.4 }

/* ── "What it costs" on a service with no listed price ────────────────────
   One row: the answer on the left, the way to act on it on the right. It is
   deliberately not shaped like a price table -- there is no number, and a
   scaffold that looks like one would promise something the band cannot pay off.
   ────────────────────────────────────────────────────────────────────────── */
.svc-quoted {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  flex-wrap: wrap;
}

.svc-quoted-copy { flex: 1 1 24rem; min-width: 0 }
.svc-quoted-copy .section-desc { margin-bottom: 0; max-width: 40rem }
.svc-quoted .btn { flex: none }

@media (max-width: 700px) {
  /* The button goes full width rather than stranding itself on its own line. */
  .svc-quoted { gap: var(--space-4) }
  .svc-quoted .btn { width: 100%; justify-content: center }
}
/* .section-desc is centred by default -- it belongs to the centred section-head
   pattern, where margin:0 auto is right. In this left-aligned row it pushed the
   paragraph off the heading's left edge by its own auto margin. */
.svc-quoted-copy .section-desc { margin-left: 0; margin-right: 0 }

/* ── Centred trailing row: the two grids on /services ─────────────────────
   .svc-grid and .reason-grid were left out when this was built. Both are
   display:grid with justify-content:center, which does nothing for a short
   last row -- 1fr tracks consume all the free space, so there is nothing for
   justify-content to distribute, and the stranded card sits hard left under
   column one. Four services in a three-column grid, and five reasons, both
   show it.

   Same flex mechanism as the other six: each item carries a basis of exactly
   (100% - gaps) / columns, so a full row still fills the line and only the
   short one moves. --grid-gap must match each grid's own column gap, since the
   basis subtracts it; both use --space-3.

   The grid-template-columns rules already on these classes stay harmless --
   a flex container ignores them.
   ────────────────────────────────────────────────────────────────────────── */
.svc-grid, .reason-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: flex-start;
  --grid-gap: var(--space-3);
  --cols: 3;
  --cols-md: 2;
  --cols-sm: 1;
}

.svc-grid > *, .reason-grid > * {
  flex: 0 1 calc((100% - (var(--cols, 3) - 1) * var(--grid-gap)) / var(--cols, 3));
  min-width: 0;
}

@media (max-width: 900px) {
  .svc-grid > *, .reason-grid > * {
    flex-basis: calc((100% - (var(--cols-md, 2) - 1) * var(--grid-gap)) / var(--cols-md, 2));
  }
}

@media (max-width: 560px) {
  .svc-grid > *, .reason-grid > * { flex-basis: 100% }
}

/* ── More after the ask, on a service page ──────────────────────────────
   Three card grids used to sit between the reader and the CTA. This is what
   replaced them: the same links, below the ask, as columns of plain links.
   Cards are built to be clicked and were competing with the conversion; after
   the CTA this is navigation, and navigation that looks like an offer competes
   with the offer.
   ────────────────────────────────────────────────────────────────────────── */
.svc-more {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: var(--space-5);
}

.svc-more-head {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--color-text-faint);
  margin: 0 0 var(--space-2);
}

.svc-more-list { list-style: none; margin: 0; padding: 0 }
.svc-more-list li + li { margin-top: 6px }

.svc-more-list a {
  font-size: .92rem;
  line-height: 1.5;
  color: var(--color-text-dim);
  text-decoration: none;
  transition: color .18s ease;
}
.svc-more-list a:hover { color: var(--color-brand) }
.svc-more-list a:focus-visible { outline: 2px solid var(--color-brand); outline-offset: 3px }

/* The links sit inside the closing band, under the ask. A rule separates them
   so the two read as two things in one band rather than one confused block. */
.cta-banner + .svc-more,
.svc-more {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
}

/* --color-text-faint (.28 alpha) measured 2.37:1 on the closing band, against
   the 4.5:1 AA needs for text this size. It is a decorative token and these are
   column headings people read. */
.svc-more-head { color: rgba(255, 255, 255, .58) }
