/* ---------------------------------------------------------------------------
   jacobbeattype.com — shared design system, dark.

   Structure follows clbengineering.com: centred section headings over a narrow
   lede, cards on alternating bands, 8px radii, a sticky bar with centred links
   and one filled button on the right.

   The palette does not. This runs dark, on the same navy the maps and the
   business card use, with the FEMA-floodplain amber as the single accent. The
   maps are dark satellite imagery, so a dark page lets them sit in the layout
   instead of punching a hole in it.
   --------------------------------------------------------------------------- */

:root{
  --bg:#0A1626;          /* page */
  --band:#0E2136;        /* alternating section band */
  --surface:#12293C;     /* cards */
  --surface-2:#16334A;   /* card hover / raised */
  --ground:#081320;      /* bar and footer, darkest */
  --head:#FFFFFF;        /* headings */
  --ink:#DCE6F0;         /* body copy */
  --slate:#94A9BE;       /* secondary copy */
  --line:rgba(255,255,255,.11);
  --line-2:rgba(255,255,255,.18);
  --accent:#E7B354;      /* FEMA-floodplain amber */
  --accent-2:#F0C471;
  --pad:clamp(20px,5vw,64px);
  --maxw:1120px;
  --shadow:0 1px 2px rgba(0,0,0,.30), 0 10px 30px rgba(0,0,0,.28);
}

*{box-sizing:border-box}
html{scroll-behavior:smooth;background:var(--bg)}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
  font-size:16.5px;line-height:1.68;-webkit-font-smoothing:antialiased;
}
h1,h2,h3{font-family:Inter,ui-sans-serif,system-ui,sans-serif;
  color:var(--head);font-weight:700;letter-spacing:-.02em;line-height:1.15;margin:0}
a{color:inherit}
:focus-visible{outline:2.5px solid var(--accent);outline-offset:3px;border-radius:3px}
img{max-width:100%}

/* ---------- sticky bar ---------- */
.bar{position:sticky;top:0;z-index:50;background:rgba(8,19,32,.88);
  backdrop-filter:saturate(1.4) blur(12px);border-bottom:1px solid var(--line)}
.bar-in{max-width:var(--maxw);margin:0 auto;padding:14px var(--pad);
  display:flex;align-items:center;gap:18px}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none;flex:0 0 auto}
.brand img{width:38px;height:38px;border-radius:50%}
.brand b{display:block;color:var(--head);font-size:15px;font-weight:700;letter-spacing:-.01em}
.brand span{display:block;color:var(--slate);font-size:12.5px;font-weight:400;line-height:1.3}
.bar nav{margin:0 auto;display:flex;gap:26px}
.bar nav a{text-decoration:none;color:var(--ink);font-size:14.5px;font-weight:500;
  padding:4px 0;border-bottom:2px solid transparent}
.bar nav a:hover{color:var(--head);border-bottom-color:var(--accent)}
.bar nav a[aria-current="page"]{color:var(--head);border-bottom-color:var(--accent)}

.btn{display:inline-flex;align-items:center;gap:8px;text-decoration:none;
  background:var(--accent);color:#14212E;font-size:14.5px;font-weight:600;
  padding:10px 18px;border-radius:8px;border:2px solid var(--accent);white-space:nowrap}
.btn:hover{background:var(--accent-2);border-color:var(--accent-2)}
.btn-ghost{background:transparent;color:var(--head);border-color:var(--line-2)}
.btn-ghost:hover{background:rgba(255,255,255,.08);border-color:var(--head)}
/* Access-code entry on a model card. A plain GET form pointed at the gated path
   produces /model/<name>/?key=..., which is precisely what middleware.js already
   redeems: it sets the cookie and bounces to the clean URL. No JS, no new
   endpoint, and nothing in the gate had to change. */
.keyform{display:flex;gap:8px;margin-top:20px;align-items:stretch}
.keyform input{flex:1;min-width:0;background:var(--ground);color:var(--ink);
  border:1px solid var(--line-2);border-radius:8px;padding:9px 12px;
  font:inherit;font-size:14px}
.keyform input::placeholder{color:var(--ink-3,#6D84A1)}
.keyform input:focus{outline:2px solid var(--accent);outline-offset:1px}
.keyform .btn{white-space:nowrap;padding:9px 15px}

/* ---------- sections ---------- */
.sec{padding:clamp(56px,7.5vw,104px) var(--pad)}
.sec.band{background:var(--band);border-block:1px solid var(--line)}
.sec.dark{background:var(--ground);border-block:1px solid var(--line)}
.sec-in{max-width:var(--maxw);margin:0 auto}
.sec-head{text-align:center;max-width:720px;margin:0 auto clamp(34px,4.5vw,56px)}
/* The page title on Services, Work and About is an h1 for landmark and search
   reasons; it must look identical to the h2 section titles around it. */
.sec-head h1,
.sec-head h2{font-size:clamp(25px,3.2vw,36px)}
.sec-head h1{margin:0}
.lede{color:var(--slate);margin:14px 0 0;font-size:17px}
.eyebrow{font-size:12px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  color:var(--accent);margin-bottom:12px}

/* ---------- cards ---------- */
.cards{display:grid;gap:clamp(16px,2vw,24px);
  grid-template-columns:repeat(auto-fit,minmax(270px,1fr))}
.card{background:var(--surface);border:1px solid var(--line);border-radius:8px;
  padding:clamp(22px,2.4vw,30px);box-shadow:var(--shadow)}
.card h2,.card h3{font-size:18.5px;margin-bottom:8px}
.card p{margin:0;color:var(--slate);font-size:15.5px}
/* Buttons carry their own colours. Without :not(.btn) this rule outranks .btn
   (both one class, this one later and more specific) and repaints a solid
   button's label amber ON amber, which reads as a blank amber block. */
.card a:not(.btn){color:var(--accent)}
/* Scale label under a card title on the Work page. Took over from the old
   "Proves it ..." eyebrow, which sat above the title and announced the point
   instead of letting the numbers make it. */
.card .scale{font-size:12px;font-weight:700;color:var(--accent);
  letter-spacing:.13em;text-transform:uppercase;margin:0 0 11px}
.sec.band .card,.sec.dark .card{background:var(--surface)}

/* four items read better as 2x2 than as 3 + 1 orphan */
@media (min-width:900px){ .cards.two{grid-template-columns:repeat(2,1fr)} }

a.card{text-decoration:none;display:block;transition:border-color .15s ease, transform .15s ease}
a.card:hover{border-color:var(--accent);transform:translateY(-2px)}

/* portfolio screenshot: bleeds to the card edge, sits above the tier label */
/* Full-bleed image band at the top of a card.
   max-width:none matters: the global img{max-width:100%} was clamping the calc,
   so on a phone the picture sat 45px short of the card's right edge. */
.card .shotwrap{position:relative;overflow:hidden;
  width:calc(100% + 2*clamp(22px,2.4vw,30px));max-width:none;
  margin:calc(-1*clamp(22px,2.4vw,30px)) calc(-1*clamp(22px,2.4vw,30px)) 18px;
  border-bottom:1px solid var(--line);background:#0A1626}
.card .shot{display:block;width:100%;max-width:none;height:210px;
  object-fit:cover;object-position:center}
@media (max-width:620px){ .card .shot{height:230px} }

/* The key rides on the picture as HTML rather than being burned into the JPEG.
   Baked in, it was cropped by object-fit and scaled to ~7px; here it stays crisp
   at any card width and matches the hero's treatment. */
.shotkey{position:absolute;left:0;top:10px;z-index:2;
  background:rgba(10,24,40,.90);border:1px solid rgba(255,255,255,.12);border-left:0;
  border-radius:0 8px 8px 0;padding:8px 11px 8px 9px;
  font-size:10.5px;font-weight:600;color:#DCE6F0;line-height:1.55;letter-spacing:-.01em}
.shotkey div{display:flex;align-items:center;gap:7px;white-space:nowrap}
.shotkey i{flex:0 0 auto;display:inline-block}
.shotkey .k-line{width:16px;height:4px;border-radius:2px;background:#E7B354}
.shotkey .k-depth{width:16px;height:11px;border-radius:2px;
  background:linear-gradient(90deg,#8CC3F0,#0A2382)}
.shotkey .k-in{width:11px;height:11px;border-radius:50%;background:#ffb87a;
  border:1.5px solid #ffd2a3}
.shotkey .k-out{width:11px;height:11px;border-radius:50%;background:#e64545;
  border:1.5px solid #ff8080}

/* ---------- services as a progression, not five equal boxes ----------
   Stacked, each carrying its role in the chain. A grid of equal cards reads as a
   generalist; a sequence reads as a system.

   The gutter used to hold 01-04. Numbers implied a ranking that does not exist
   and made the reader count instead of look, so each line now carries a mark of
   what it actually is: a water surface, map layers, a depth-damage curve, a
   dollar. The SVGs draw with currentColor and inherit the amber. */
.prog{list-style:none;margin:0;padding:0;max-width:860px;margin-inline:auto}
.prog li{position:relative;padding:26px 0 26px 62px;
  border-top:1px solid var(--line)}
.prog li:last-child{border-bottom:1px solid var(--line)}
.prog-ico{position:absolute;left:0;top:26px;width:34px;height:34px;
  color:var(--accent);flex:none}
.prog-ico svg{display:block;width:100%;height:100%}
.prog .role{font-size:11.5px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  color:var(--slate);margin-bottom:7px}
.prog h2,.prog h3{font-size:20px;margin-bottom:9px}
.prog p{margin:0;color:var(--slate);font-size:15.5px}
@media (max-width:600px){
  .prog li{padding-left:46px}
  .prog-ico{width:28px;height:28px;top:27px}
}

/* Automation, deliberately not numbered: it underpins the four above rather than
   following them. Amber left rule instead of a counter, so it reads as a foundation. */
.prog-under{max-width:860px;margin:18px auto 0;padding:22px 0 22px 62px;
  border-left:2px solid var(--accent);padding-left:60px}
.prog-under h2,.prog-under h3{font-size:20px;margin-bottom:9px}
.prog-under p{margin:0;color:var(--slate);font-size:15.5px}
@media (max-width:600px){ .prog-under{padding-left:22px} }

/* ---------- the DATA -> MODELS -> MAPS -> DECISIONS chain ----------
   Four stages with an arrow between them, and automation as the bar underneath
   that carries all four. Built in CSS rather than as an image so it reflows on a
   phone into a stack and stays legible at any width. */
.chain{list-style:none;margin:0;padding:0;display:grid;gap:14px;
  grid-template-columns:repeat(4,1fr);counter-reset:c;position:relative}
/* One hairline running the full width, behind the cards. The cards are opaque, so
   it only shows in the gaps: the four then read as one pipeline rather than four
   separate boxes, without drawing anything that competes with the text. */
.chain::before{content:"";position:absolute;left:0;right:0;top:50%;height:1px;z-index:0;
  background:linear-gradient(90deg,transparent 0,rgba(231,179,84,.40) 14%,
    rgba(231,179,84,.40) 86%,transparent 100%)}
.chain li{position:relative;z-index:1;background:var(--surface);border:1px solid var(--line);
  border-radius:8px;padding:20px;box-shadow:var(--shadow)}
/* Same line-drawn marks as the Services list, so a reader who has seen one page
   recognises the other. currentColor, so they inherit the amber. */
/* .chain span sets --slate on every span in here and outranks a lone class, so
   this has to be at least as specific or the marks come out grey. */
.chain li > .chain-ico{display:block;width:26px;height:26px;color:var(--accent);
  margin-bottom:13px}
.chain-ico svg{display:block;width:100%;height:100%}
.chain li::after{content:"";position:absolute;right:-11px;top:50%;width:10px;height:10px;
  border-top:2px solid var(--accent);border-right:2px solid var(--accent);
  transform:translateY(-50%) rotate(45deg);z-index:2}
.chain li:last-child::after{display:none}
.chain b{display:block;color:var(--accent);font-size:12px;font-weight:700;
  letter-spacing:.16em;text-transform:uppercase;margin-bottom:9px}
.chain span{display:block;color:var(--slate);font-size:14.5px;line-height:1.5}
/* Automation is the plinth under the four, not a fifth stage. A full amber box
   made it compete as an equal; an amber top edge with quiet sides reads as the
   thing the row above stands on. */
.chain-base{margin-top:14px;background:var(--surface);
  border:1px solid var(--line);border-top:3px solid var(--accent);
  border-radius:8px;padding:19px 22px;display:flex;flex-wrap:wrap;gap:6px 22px;
  align-items:baseline}
.chain-base b{color:var(--accent);font-size:12px;font-weight:700;letter-spacing:.16em;
  text-transform:uppercase;flex:0 0 auto}
.chain-base span{color:var(--slate);font-size:14.5px;line-height:1.55;flex:1;min-width:260px}
@media (max-width:860px){
  .chain{grid-template-columns:1fr}
  .chain::before{display:none}   /* the rail is horizontal; the stack is not */
  .chain li::after{right:50%;top:auto;bottom:-13px;transform:translateX(50%) rotate(135deg)}
}

/* ---------- stat row ---------- */
.stats{display:flex;flex-wrap:wrap;gap:clamp(22px,4vw,54px);margin:22px 0 0}
.stat .n{font-size:clamp(25px,3vw,33px);font-weight:700;color:var(--head);
  letter-spacing:-.02em;line-height:1}
.stat .l{color:var(--slate);font-size:13.5px;margin-top:5px;max-width:20ch;line-height:1.4}

/* ---------- definition rows ---------- */
.rows{margin:0;padding:0;list-style:none}
.rows li{padding:16px 0;border-top:1px solid var(--line);color:var(--slate);font-size:15.5px}
.rows li:last-child{border-bottom:1px solid var(--line)}
.rows b{color:var(--head);font-weight:600}

/* ---------- a map presented as evidence ---------- */
.figure{max-width:var(--maxw);margin:0 auto;border:1px solid var(--line);
  border-radius:10px;overflow:hidden;box-shadow:var(--shadow);background:var(--surface)}
.figure img{display:block;width:100%;height:auto}
.figure figcaption{padding:16px clamp(16px,2vw,24px);color:var(--slate);font-size:13.5px;
  border-top:1px solid var(--line)}
.figcap{padding:16px clamp(16px,2vw,24px);color:var(--slate);font-size:13.5px;
  border-top:1px solid var(--line);line-height:1.6}
.figcap b{color:var(--head)}
.figcap a{color:var(--accent);text-decoration:none;white-space:nowrap}
.figcap a:hover{text-decoration:underline}
.mapkey{display:flex;flex-wrap:wrap;gap:8px 22px;padding:14px clamp(16px,2vw,24px);
  border-top:1px solid var(--line);font-size:13px;color:var(--ink)}
.mapkey span{display:flex;align-items:center;gap:8px}
.mapkey i{width:13px;height:13px;border-radius:3px;display:inline-block}

/* ---------- footer ---------- */
.foot{background:var(--ground);color:var(--slate);padding:40px var(--pad) 52px;
  font-size:13.5px;border-top:1px solid var(--line)}
/* The flex row was built for several footer blocks, but there is only one. With
   one child, flex-start plus a 62ch cap pinned the whole disclaimer into a
   narrow column in the bottom-left corner while every other section on the page
   was centred. Centre it and let it use the width. */
.foot-in{max-width:var(--maxw);margin:0 auto;display:flex;flex-wrap:wrap;gap:18px 40px;
  align-items:flex-start;justify-content:center;text-align:center}
.foot a{color:var(--ink)}
.foot p{margin:0;max-width:78ch}

@media (max-width:820px){
  .bar-in{flex-wrap:wrap;gap:12px}
  .bar nav{margin:0;order:3;width:100%;gap:20px;overflow-x:auto}
  /* The bar's CTA goes to /work/, and "Work" is in the nav directly beneath it.
     On a phone the bar wraps, so that duplicate cost a full button row above the
     fold. It stays on desktop, where it sits inline and costs nothing. */
  .bar .btn{display:none}
}


/* A qualification that has to be present but must not compete with the finding it
   qualifies. Same caption block, visually demoted. */
.figcap .qual{display:block;margin-top:7px;font-size:12.5px;color:var(--slate);opacity:.85}