/* Emgrand Intelligence marketing site — shared design system.
   Implements the reviewed redesign mockup; the rules here are the
   codification in docs/design/emgrand-brand-style-guide.md.

   BUMP THE ?v= ON EVERY PAGE'S <link> WHEN THIS FILE CHANGES.
   website.caddy serves /srv/website with `Cache-Control: public,
   max-age=3600` and caddy's file_server does not content-hash names, so
   that query string is the only cache key this stylesheet has - exactly
   the situation projects/templates/base.html documents for app.css. Ship
   a change without bumping it and returning visitors keep the old sheet
   for up to an hour against new markup. All three pages must move
   together, since they share this file; edge-check.yml fails the build
   if one of them disagrees or drops the key.

   The PNGs have no cache key and do not need one while they are
   append-only: replacing a logo IN PLACE would go stale the same way,
   so ship a new filename rather than overwriting one.

   ONE stylesheet, not a copy per page. index.html, ellicott.html and
   ellicott-alta.html all link this file rather than each inlining a copy
   of it - three hand-synchronised copies of a token set is a standing
   invitation to drift. (The app's 404.html/500.html do duplicate
   app.css's tokens, but that is deliberate and load-bearing: those
   pages must render with no static pipeline available, so they cannot
   link anything. They are not an example of drift - they have stayed in
   sync - just of the cost this file avoids paying three more times.)

   Serving this needs `style-src 'self'` in website/caddy/website.caddy -
   an external sheet is NOT covered by 'unsafe-inline' - and that header
   string is asserted verbatim by .github/workflows/edge-check.yml, so
   the caddy config and the workflow have to change together with this
   file's existence.

   'unsafe-inline' is still required alongside 'self', for exactly one
   thing: the hero pin circles, which are positioned with inline style=
   attributes because each placement is a one-off rather than a reusable
   class. If those ever become classes, drop 'unsafe-inline' in the same
   change - and grep the pages first, since any other inline style=
   would go dark silently.

   Brand rule, RETIRED 2026-09-04 (Andre, on adopting the Ellicott logo
   package): brass no longer belongs to EmGrand alone. Every asset in
   that package is gold-on-black with no second accent colour, so
   Ellicott's own site-wide accent (buttons, badges, links) is brass
   too now, rather than a distinct colour the way Vectra's silver was.
   The rule below describes the arrangement THIS FILE HAD from its
   original authoring until that date, kept as history rather than
   deleted, since the tokens and reasoning it names (silver, the
   never-mix clause) no longer exist below and a reader hitting this
   comment cold needs to know why:

   "Brass is EmGrand's accent and owns the global chrome; silver is
   Vectra's and appears only on Vectra surfaces (and in the Vectra card
   on the EmGrand homepage). They never mix inside one component - the
   logo mark is the one place gold and chrome sit together, and it
   carries its own gradient."

   Contrast (WCAG AA, computed - see the ratios in each pairing below,
   not eyeballed): --text-on-ink 15.62:1 and --muted-on-ink 6.81:1 on
   --ink; 14.13:1 / 6.16:1 on --ink-2. --text-on-paper 14.21:1 and
   --muted-on-paper 5.73:1 on --paper. --brass 7.15:1 on --ink, 6.47:1
   on --ink-2. Text on a brass fill uses near-black ink (7.27:1); white
   on brass is 2.56:1 and is never used.

   --brass-deep exists because plain --brass fails on light backgrounds
   (2.33:1 on --paper) - nowhere near 4.5:1. It is a darker shade of the
   SAME hue (same colour the mark was sampled from, not a second
   invented colour), computed for headroom rather than a bare pass:
   7.81:1 on --paper, 7.06:1 on --paper-2, 7.06:1 on --brass-tint.
   --brass-tint is the pale fill --brass-deep text sits on for chips/
   icon backgrounds; the badge that composites --brass-deep at 10%
   opacity over --paper reads at 6.70:1, not the flat pairing above -
   compute the actual composited colour for a translucent background,
   the flat pair is not what a reader sees. Plain --brass stays fine for
   BORDERS on light surfaces (a UI/decorative pairing, the 3:1 non-text
   guideline, not 4.5:1) - .chip.out's border on its own --brass-tint
   fill is 2.11:1, which is what this same pairing was at 1.71:1 in
   silver, so this is not a regression the retirement introduced.

   The three .badge variants sit on a tint of their own colour, so they
   run lower than the body pairings and have to be quoted separately:
   .badge.live 4.93:1, .badge.soon 4.89:1, .badge.on-paper 6.70:1 (was
   6.64:1 under silver - recomputed, not carried over, because the badge
   composites a translucent fill and the flat-pair number is not it).
   .badge.soon is the floor of the whole system - an earlier version of
   this comment claimed the floor was --muted-on-paper on --paper-2 at
   5.18:1, which was wrong, and the same sentence went out in the PR
   description. Everything here clears 4.5:1 AA for normal text; nothing
   is quoted from memory - recompute before editing a colour, and quote
   the pairing on the surface it is actually used on, which is how a
   silver badge shipped onto a white card at 1.74:1 under the old rule. */

:root{
  --ink:#12151b; --ink-2:#1a1f27; --ink-3:#232a34;
  --paper:#f4f5f0; --paper-2:#e9eae2;
  --brass:#c89b3c; --brass-deep:#5f491b; --brass-tint:#eee9dd;
  --text-on-paper:#20242b; --muted-on-paper:#5c6259;
  --text-on-ink:#eceee6; --muted-on-ink:#9aa093;
  --line-on-paper:rgba(18,21,27,.10); --line-on-ink:rgba(236,238,230,.14);
  --font-display:Georgia,"Iowan Old Style","Palatino Linotype","Book Antiqua",serif;
  --font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;
  --font-mono:ui-monospace,"SF Mono",Consolas,"Cascadia Code",monospace;
}
*{box-sizing:border-box;}
body{margin:0; font-family:var(--font-sans); background:var(--paper); color:var(--text-on-paper);}
img{max-width:100%;}
section{padding:76px 40px;}
.inner{max-width:1180px; margin:0 auto;}
.eyebrow{font-family:var(--font-mono); font-size:.72rem; text-transform:uppercase; letter-spacing:.14em;}

/* ---------- buttons: one solid per view, everything else ghost ---------- */
.btn{
  display:inline-flex; align-items:center; gap:8px; font-family:var(--font-sans);
  font-weight:600; font-size:.92rem; padding:13px 24px; border-radius:3px;
  text-decoration:none; border:1px solid transparent;
}
.btn-brass{background:var(--brass); color:#191204;}
.btn-brass:hover{background:#d8ac4d;}
.btn-ghost-ink{background:transparent; color:var(--text-on-ink); border-color:var(--line-on-ink);}
.btn-ghost-ink:hover{background:var(--ink-3);}
.btn-ghost-paper{background:transparent; color:var(--text-on-paper); border-color:var(--line-on-paper);}
.btn-ghost-paper:hover{background:var(--paper-2);}

.chip{
  font-family:var(--font-mono); font-size:.72rem; padding:5px 10px; border-radius:3px;
  background:var(--paper-2); color:var(--text-on-paper); border:1px solid var(--line-on-paper);
}
/* accent-tinted chip = "this is what you get out", vs neutral = "what you put in" */
.chip.out{background:var(--brass-tint); border-color:var(--brass);}

/* ---------- global nav (EmGrand chrome, every page) ---------- */
.gnav{background:var(--ink); color:var(--text-on-ink); padding:0 40px;}
.gnav-row{max-width:1180px; margin:0 auto; display:flex; align-items:center; justify-content:space-between; height:64px;}
.brand-lockup{display:flex; align-items:center; gap:10px; font-family:var(--font-display); font-size:1.05rem; font-weight:700; letter-spacing:.01em; color:var(--text-on-ink); text-decoration:none;}
/* EmGrand has no real logo asset yet - the circle monogram is the
   documented stand-in, and stays until a parent-brand mark exists. */
.brand-mark{
  width:26px; height:26px; border:1.5px solid var(--brass); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--font-mono); font-size:.62rem; color:var(--brass); flex:none;
}
.gnav-links{display:flex; align-items:center; gap:30px; font-size:.86rem; font-weight:500;}
.gnav-links a{color:var(--text-on-ink); text-decoration:none; opacity:.86;}
.gnav-links a:hover{opacity:1;}
.navdrop{position:relative;}
.navdrop-panel{
  display:none; position:absolute; top:38px; left:-14px; background:var(--ink-2);
  border:1px solid var(--line-on-ink); border-radius:8px; padding:8px; min-width:230px;
  box-shadow:0 18px 40px rgba(0,0,0,.35); z-index:5;
}
/* :focus-within as well as :hover - the site's CSP forbids script, so a
   hover-only dropdown would be permanently unreachable by keyboard. */
.navdrop:hover .navdrop-panel, .navdrop:focus-within .navdrop-panel{display:block;}
.navdrop-item{
  display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:9px 10px; border-radius:5px; font-size:.82rem; text-decoration:none; color:var(--text-on-ink);
}
.navdrop-item:hover{background:var(--ink-3);}
.navdrop-item .tag{font-family:var(--font-mono); font-size:.6rem; text-transform:uppercase; letter-spacing:.06em; color:var(--muted-on-ink);}
.navdrop-item.live .tag{color:var(--brass);}
.navdrop-item .sysname{display:flex; align-items:center; gap:8px;}
.navdrop-item .mark-img{height:15px; width:auto;}
.cta-login{font-size:.84rem; padding:8px 16px; border:1px solid var(--line-on-ink); border-radius:3px; color:var(--text-on-ink); text-decoration:none; white-space:nowrap;}
.cta-login:hover{background:var(--ink-3);}

/* ---------- product sub-nav (only on pages inside one system) ---------- */
.subnav{background:var(--paper-2); border-bottom:1px solid var(--line-on-paper);}
.subnav-row{max-width:1180px; margin:0 auto; padding:0 40px; height:52px; display:flex; align-items:center; justify-content:space-between; gap:20px;}
.subnav-id{display:flex; align-items:center; gap:9px; font-family:var(--font-mono); font-size:.76rem; color:var(--muted-on-paper);}
.subnav-id b{font-family:var(--font-sans); color:var(--brass-deep); font-weight:700;}
.subnav-id .mark-img{height:28px; width:auto;}
.subnav-links{display:flex; gap:22px; font-size:.8rem; font-weight:600;}
.subnav-links a{text-decoration:none; color:var(--muted-on-paper);}
.subnav-links a.on{color:var(--brass-deep);}

/* ---------- hero ---------- */
.hero{background:var(--ink); color:var(--text-on-ink); position:relative; overflow:hidden; padding:76px 40px;}
/* the plat/coordinate-grid motif - hero only; at this density it
   competes with body text anywhere else */
.grid-tex{
  background-image:
    linear-gradient(var(--line-on-ink) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-on-ink) 1px, transparent 1px);
  background-size:42px 42px;
}
.hero .inner{position:relative; z-index:1;}
/* survey-monument motif: always bleeding off an edge, one or two per page */
.pin{position:absolute; border-radius:50%; border:1px solid var(--brass); opacity:.5;}
.hero-eyebrow{color:var(--brass);}
.hero h1{font-family:var(--font-display); font-size:clamp(2.2rem,4.4vw,3.6rem); line-height:1.08; margin:16px 0 20px; max-width:15ch; text-wrap:balance;}
.hero p.lede{font-size:1.1rem; line-height:1.6; color:var(--muted-on-ink); max-width:56ch; margin:0 0 34px;}
.hero-actions{display:flex; gap:14px; flex-wrap:wrap;}
.hero-logo{height:118px; width:auto; display:block; margin:0 0 22px;}

/* ---------- featured systems grid ---------- */
.sys-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:18px; margin-top:44px;}
.sys-card{background:var(--ink-2); border:1px solid var(--line-on-ink); border-radius:10px; padding:26px 22px; color:var(--text-on-ink); display:flex; flex-direction:column;}
/* Shown, not hidden: dashed border + a muted title. The mockup dimmed the
   whole card with opacity:.55, which composites the .85rem body copy down
   to 2.77:1 against the hero ink - a WCAG AA failure (needs 4.5:1).
   Recovering it by raising the opacity takes .85+, at which point nothing
   reads as dimmed at all, so the muting comes from the title color
   instead. The live card keeps a full-brightness serif title, the logo
   mark and a brass link, which is what carries the distinction. */
.sys-card.soon{border-style:dashed;}
.sys-card.soon h3{color:var(--muted-on-ink);}
.sys-card .num{font-family:var(--font-mono); font-size:.7rem; color:var(--muted-on-ink); letter-spacing:.08em;}
.sys-card h3{font-family:var(--font-display); font-size:1.28rem; margin:12px 0 6px;}
.sys-card p{font-size:.85rem; color:var(--muted-on-ink); line-height:1.55; margin:0 0 16px;}
/* align-self:flex-start, not just width:auto: .sys-card is a column flex
   container with no align-items, so the default `stretch` overrides an
   auto cross-size (width IS "auto" here, just not a literal absent
   value) to fill the card - it shipped rendering at 236x32 against a
   118px-wide card, a 220x186 mark stretched to a 7.36 ratio. .badge
   below already carries this same override for the same reason; this
   rule needed it too and did not have it. */
.sys-card .mark-img{height:32px; width:auto; align-self:flex-start; margin:2px 0 10px;}
.badge{font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.08em; padding:4px 9px; border-radius:20px; align-self:flex-start;}
/* roadmap state, never a warning/red hue.
   These two are INK-SURFACE ONLY (the system cards in the hero): both
   tint their own accent over --ink-2, so on a light card the same rule
   would put a pale accent on near-white. `.badge.on-paper` below is the
   light-surface variant - gate-1 caught a silver-on-paper badge at
   1.74:1 shipping because the ink rule was reused on a .mod-card. */
.badge.live{background:rgba(200,155,60,.16); color:var(--brass);}
.badge.soon{background:rgba(154,160,147,.14); color:var(--muted-on-ink);}
/* light-surface badge: graphite on a graphite tint over --paper, 6.64:1 */
.badge.on-paper{background:rgba(95,73,27,.10); color:var(--brass-deep);}
.sys-link{font-size:.82rem; font-weight:700; color:var(--brass); text-decoration:none; margin-top:auto;}
.sys-link:hover{text-decoration:underline;}
.sys-note{font-size:.82rem; font-weight:700; color:var(--muted-on-ink); margin-top:auto;}

/* ---------- light bands ---------- */
.light{background:var(--paper);}
.light.alt{background:var(--paper-2);}
.light h2{font-family:var(--font-display); font-size:2rem; margin:0 0 14px; text-wrap:balance;}
.light .kicker{color:var(--brass-deep); display:block;}
.light p.intro{color:var(--muted-on-paper); line-height:1.65; font-size:.96rem; margin:0; max-width:64ch;}
.col2{display:grid; grid-template-columns:1fr 1.15fr; gap:60px; align-items:start;}

.feat-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:20px; margin-top:40px;}
.col2 .feat-grid{grid-template-columns:1fr; gap:16px; margin-top:0;}
.feat{background:var(--paper); border:1px solid var(--line-on-paper); border-radius:10px; padding:22px;}
.feat .ic{
  width:34px; height:34px; border-radius:8px; background:var(--brass-tint); color:var(--brass-deep);
  display:flex; align-items:center; justify-content:center; font-family:var(--font-mono); font-size:.8rem; margin-bottom:14px;
}
.feat h4{margin:0 0 6px; font-size:.98rem;}
.feat p{margin:0; font-size:.84rem; color:var(--muted-on-paper); line-height:1.5;}

/* numbered steps: only for a genuine sequence, never a parallel set */
.steps{display:grid; grid-template-columns:repeat(4,1fr); gap:0; margin-top:40px; border-top:1px solid var(--line-on-paper);}
.step{padding:26px 20px 0; border-left:1px solid var(--line-on-paper);}
.step:first-child{border-left:none;}
.step .n{font-family:var(--font-mono); color:var(--brass-deep); font-size:.78rem; letter-spacing:.06em;}
.step h4{font-size:1rem; margin:8px 0 6px;}
.step p{font-size:.85rem; color:var(--muted-on-paper); line-height:1.55; margin:0;}
.steps.stacked{grid-template-columns:1fr; border-top:none; margin-top:0;}
.steps.stacked .step{border-left:2px solid var(--brass); padding:0 0 22px 20px;}
.steps.stacked .step:last-child{padding-bottom:0;}

.module-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-top:40px;}
.mod-card{background:var(--paper); border:1px solid var(--line-on-paper); border-radius:10px; padding:20px; display:flex; flex-direction:column; gap:10px;}
.mod-card h4{margin:0; font-size:.95rem;}
.mod-card p{margin:0; font-size:.8rem; color:var(--muted-on-paper); line-height:1.5;}
.mod-card .go{font-size:.78rem; font-weight:700; color:var(--brass-deep); text-decoration:none; margin-top:auto;}
.mod-card .state{font-family:var(--font-mono); font-size:.62rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted-on-paper); margin-top:auto;}
/* a solid ring, not a stripe or single-side border */
.mod-card.current{border-color:var(--brass); box-shadow:0 0 0 1px var(--brass);}
.mod-card.soon{border-style:dashed;}

.format-panel{background:var(--paper-2); border-radius:12px; padding:36px; margin-top:44px; display:grid; grid-template-columns:1fr 1fr; gap:40px;}
.format-panel.single{grid-template-columns:1fr;}
.light.alt .format-panel{background:var(--paper);}
.format-col .lbl{font-family:var(--font-mono); font-size:.72rem; text-transform:uppercase; letter-spacing:.1em; color:var(--muted-on-paper); margin-bottom:14px;}
.format-col .chips{display:flex; flex-wrap:wrap; gap:8px;}
.format-col .foot{font-size:.78rem; color:var(--muted-on-paper); margin:14px 0 0; line-height:1.5;}

.deliverables{margin:16px 0 0; padding:0; display:flex; flex-direction:column;}
.deliverables li{list-style:none; display:flex; gap:12px; padding:14px 0; border-top:1px solid var(--line-on-paper); font-size:.9rem; align-items:baseline;}
.deliverables li:last-child{border-bottom:1px solid var(--line-on-paper);}
.deliverables .mk{font-family:var(--font-mono); font-size:.75rem; color:var(--brass-deep); flex-shrink:0; width:2ch;}

/* ---------- closing CTA + footer ---------- */
.cta-band{background:var(--ink); color:var(--text-on-ink);}
.cta-band .inner{display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:24px;}
.cta-band h2{font-family:var(--font-display); font-size:1.8rem; margin:0 0 10px; text-wrap:balance;}
.cta-band p{color:var(--muted-on-ink); margin:0; max-width:50ch;}
footer.site-foot{background:var(--ink); color:var(--muted-on-ink); padding:34px 40px; font-size:.78rem; border-top:1px solid var(--line-on-ink);}
footer.site-foot .inner{display:flex; justify-content:space-between; flex-wrap:wrap; gap:14px;}
footer.site-foot a{color:var(--brass);}
footer.site-foot .fine{margin:14px 0 0; max-width:80ch; line-height:1.55;}

/* single breakpoint - grids collapse to one column together */
@media (max-width:860px){
  .sys-grid, .feat-grid, .module-grid, .steps{grid-template-columns:1fr;}
  .col2{grid-template-columns:1fr; gap:36px;}
  .format-panel{grid-template-columns:1fr; gap:28px; padding:26px;}
  .steps{border-top:none;}
  .step{border-left:none; padding:0 0 22px;}
  section, .hero{padding:52px 20px;}
  .gnav{padding-left:20px; padding-right:20px;}
  /* The mockup hides the nav links entirely below 860px, which it can
     afford because its chrome is a prototype. Here it would strand a
     phone visitor: the site has no JS (see the CSP note in
     website.caddy), so there is no hamburger to open and no other route
     to /ellicott.html from the homepage nav. Instead the bar wraps to two
     rows and the links reflow - no script, nothing hidden. The dropdown
     panel is flattened to inline text for the same reason: :hover is
     not a thing you can do on a touch screen. */
  .gnav-row{height:auto; flex-wrap:wrap; padding:12px 0; gap:10px 16px;}
  .gnav-links{width:100%; order:3; flex-wrap:wrap; gap:4px 18px; font-size:.82rem;}
  /* Touch targets. Nothing here inherits a hit area from the bar - the
     desktop links are only ~18px tall themselves and rely on the 64px row
     being the thing you aim at, which the wrapped mobile rows are not. So
     pad the links, and give the row `gap` so two padded targets never end
     up flush against each other. */
  .gnav-links a{padding:8px 0;}
  .navdrop{position:static; display:flex; flex-direction:column; gap:4px; width:100%;}
  .navdrop-panel{
    display:flex; position:static; flex-wrap:wrap; gap:4px 16px;
    background:none; border:none; box-shadow:none; padding:0; min-width:0;
  }
  /* This has to restate display:flex. The base rule that opens the panel
     (`.navdrop:hover .navdrop-panel`) is 0,3,0 and lives outside this
     media query, so it beats the 0,1,0 `.navdrop-panel` above and would
     silently re-stack the flattened row to display:block the moment a
     link is hovered or focused - which on a phone is any tap. */
  .navdrop:hover .navdrop-panel,
  .navdrop:focus-within .navdrop-panel{display:flex;}
  .navdrop-panel .navdrop-item{padding:8px 0; background:none;}
  .navdrop-item .tag{display:none;}
  .subnav-row{padding-left:20px; padding-right:20px; height:auto; padding-top:8px; padding-bottom:8px;}
  /* the sub-nav was left at 17px by the first pass at this - same rule,
     same reason as .gnav-links a above */
  .subnav-links{gap:4px 16px; flex-wrap:wrap;}
  .subnav-links a{padding:8px 0;}
  .hero-logo{height:88px;}
  footer.site-foot{padding:28px 20px;}
}
