/* =========================================================
   FlowLink kit — the reusable half of the FlowLink design
   language. Loaded after styles.css by every page that wants
   the dark technical surface: hairline structure, one accent,
   motion that reacts to scroll.
   Page-specific work lives in that page's own stylesheet.
   ========================================================= */

/* ---------------- tokens ---------------- */
:root{
  --mono:ui-monospace,'SFMono-Regular',Menlo,'Cascadia Mono','Segoe UI Mono',Consolas,monospace;
  --fl-line:rgba(96,148,232,.20);
  --fl-line-strong:rgba(96,148,232,.44);
  --fl-surface:linear-gradient(180deg,rgba(14,31,69,.52),rgba(6,15,38,.92));
  --fl-pad:clamp(76px,9vw,128px);
  --ease-out:cubic-bezier(.22,1,.36,1);
}

/* ---------------- scroll progress ---------------- */
.scroll-progress{
  position:fixed;top:0;left:0;right:0;height:2px;z-index:80;
  pointer-events:none;background:rgba(255,255,255,.05);
}
.scroll-progress__bar{
  display:block;height:100%;transform:scaleX(0);transform-origin:0 50%;
  background:linear-gradient(90deg,var(--pink),var(--fl-accent-mid,#ff4fb0) 45%,var(--blue));
  box-shadow:0 0 14px rgba(193,0,124,.55);will-change:transform;
}

/* ---------------- nav state ---------------- */
.nav{transition:background-color .3s ease,border-color .3s ease,box-shadow .45s ease}
.nav.is-stuck{box-shadow:0 16px 44px -30px #000}

/* ---------------- structure ---------------- */
.fl-section{padding:var(--fl-pad) 0;position:relative}
.fl-section--flush{padding-top:0}

/* Full-page measure. .shell centres content in a 1180px column; .wide runs it
   to the page edges with a single gutter instead, so a page can hold one
   consistent width rather than alternating between centred and edge-to-edge. */
.wide{width:100%;max-width:none;margin:0;padding:0 var(--bleed-gut)}
:root{--bleed-gut:clamp(24px,5vw,72px)}
/* Components that cap their own width have to release it inside .wide. */
.wide .tech-panel,
.wide .build-list,
.wide .qa,
.wide .sysview,
.wide .band__head{max-width:none}

.sec-index{
  display:flex;align-items:center;gap:14px;margin:0 0 22px;
  font-family:var(--mono);font-size:12px;letter-spacing:.2em;text-transform:uppercase;
  color:rgba(255,255,255,.55);
}
.sec-index__n{color:var(--blue)}
.sec-index::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--fl-line),transparent)}

.band__head--flush{margin-bottom:56px;max-width:820px}
.h-section--pink{color:var(--pink)}
.fl-body{margin:0 0 20px;color:var(--t-80);line-height:1.7;font-size:17px;max-width:68ch}
.fl-body:last-of-type{margin-bottom:36px}

/* ---------------- spotlight blueprint grid ---------------- */
.grid-spot{position:relative;overflow:hidden}
.grid-spot > *{position:relative;z-index:1}
.grid-spot::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;
  transition:opacity .5s ease;
  background-image:
    linear-gradient(to right, rgba(0,115,255,.34) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(0,115,255,.34) 1px, transparent 1px);
  background-size:32px 32px;
  -webkit-mask-image:radial-gradient(260px 200px at var(--mx,50%) var(--my,50%), #000 0%, rgba(0,0,0,.4) 45%, transparent 75%);
  mask-image:radial-gradient(260px 200px at var(--mx,50%) var(--my,50%), #000 0%, rgba(0,0,0,.4) 45%, transparent 75%);
}
.grid-spot:hover::before,.grid-spot:focus-within::before{opacity:1}

/* Touch devices have no cursor to follow, so in-view cards get a slow
   self-driven sweep instead of sitting there completely inert. */
@media (hover:none),(pointer:coarse){
  .grid-spot.is-inview::before{opacity:.85;animation:fl-spot-sweep 9s ease-in-out infinite}
}
@keyframes fl-spot-sweep{
  0%   {--mx:18%;--my:24%}
  50%  {--mx:82%;--my:74%}
  100% {--mx:18%;--my:24%}
}
@property --mx{syntax:'<percentage>';inherits:false;initial-value:50%}
@property --my{syntax:'<percentage>';inherits:false;initial-value:50%}

/* ---------------- panels ---------------- */
.tech-panel{
  position:relative;overflow:hidden;
  border:1px solid var(--fl-line);border-radius:var(--r-lg);
  background:var(--fl-surface);padding:60px 62px;
  max-width:var(--shell);margin:0 auto;
  transition:border-color .45s ease,box-shadow .45s ease;
}
.tech-panel:hover,.tech-panel:active{border-color:var(--fl-line-strong)}

/* ---------------- spec pills ---------------- */
.spec-strip{list-style:none;display:flex;flex-wrap:wrap;gap:10px;margin:38px 0 0;padding:0}
.spec-strip li{
  font-family:var(--mono);font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;
  color:rgba(255,255,255,.74);
  border:1px solid var(--fl-line);border-radius:var(--r-pill);
  padding:8px 15px;background:rgba(8,18,43,.55);
  transition:border-color .3s ease,color .3s ease,transform .3s var(--ease-out);
}
.spec-strip li:hover{border-color:var(--fl-line-strong);color:#fff;transform:translateY(-2px)}

/* ---------------- check rows ---------------- */
.check-rows{list-style:none;margin:0 0 40px;padding:0}
.check-rows li{
  display:flex;align-items:flex-start;gap:16px;padding:17px 0;
  border-top:1px solid var(--fl-line);
  font-size:17px;font-weight:500;line-height:1.5;color:rgba(255,255,255,.94);
}
.check-rows li:last-child{border-bottom:1px solid var(--fl-line)}
.check-rows svg{flex:none;margin-top:2px;width:22px;height:22px;color:var(--blue)}

/* ---------------- numbered build rows ---------------- */
.build-list{max-width:var(--shell);margin:0 auto;border-top:1px solid var(--fl-line)}
/* Rows that own the full page width: the gutter lives on the row itself, so
   the hairline separators still run edge to edge. Declared after .build-list
   so it actually wins — same specificity, source order decides. */
.build-list--bleed{max-width:none;margin:0}
.build-list--bleed .build-row{padding-left:var(--bleed-gut);padding-right:var(--bleed-gut)}
.build-row{
  position:relative;display:grid;grid-template-columns:104px minmax(0,1fr) 1.05fr;
  gap:36px;align-items:start;padding:34px 12px;
  border-bottom:1px solid var(--fl-line);
  transition:background-color .45s ease;
}
.build-row:not(:has(.build-row__num)){grid-template-columns:minmax(0,1fr) 1.05fr}
.build-row:hover{background:rgba(0,115,255,.05)}
.build-row::after{
  content:"";position:absolute;left:0;right:0;bottom:-1px;height:1px;
  background:linear-gradient(90deg,var(--pink),var(--blue));
  transform:scaleX(0);transform-origin:0 50%;
  transition:transform .7s var(--ease-out);
}
.build-row:hover::after{transform:scaleX(1)}
.build-row__num{
  margin:0;padding-top:5px;font-family:var(--mono);font-size:12px;letter-spacing:.2em;
  color:var(--blue);transition:color .35s ease;
}
.build-row:hover .build-row__num{color:var(--pink)}
.build-row h3{margin:0;font-size:clamp(19px,2.1vw,25px);line-height:1.25;letter-spacing:-.015em;color:#fff}

/* Branded row heading: same two-tone wordmark as each service's own hero
   (.fl-flow / .fl-link), just sized for a dense list instead of a hero —
   the hero-sized version read as too playful stacked seven times in a row.
   Colour comes from --pink, which a row shadows locally via an inline
   style (--pink:var(--acc-mind) etc.), so it also recolours this row's
   hover underline and number for free — no separate branding rules needed. */
.build-row__mark{margin:0;line-height:1.2;letter-spacing:-.01em;font-size:clamp(17px,1.7vw,20px)}
.build-row__mark .fl-flow{font-family:'Univia Pro Black','Plus Jakarta Sans',sans-serif;font-weight:800;color:#fff}
.build-row__mark .fl-link{font-family:'Univia Pro Ultra','Plus Jakarta Sans',sans-serif;font-weight:900;color:var(--pink)}
.build-row p{margin:0;color:var(--t-70);font-size:16px;line-height:1.65}
.build-row__link{
  grid-column:2 / -1;margin-top:6px;
  font-family:var(--mono);font-size:11.5px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--pink);text-decoration:none;
  transition:color .3s ease,gap .3s ease;
  display:inline-flex;align-items:center;gap:8px;
}
.build-row__link:hover{color:#fff;gap:14px}

/* ---------------- bio card — photo left, text right ----------------
   The photo used to fill the card top with the name overlapping the
   bottom edge; whenever a photo wasn't framed for that exact crop, the
   overlap or the "center top" anchor cut into the face. Photo and text
   now sit side by side with nothing drawn on top of either, so the full
   photo is always visible regardless of how it's framed. */
.pcards{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}

.pcard{
  position:relative;display:flex;align-items:stretch;min-height:236px;
  background:var(--surface-1);
  border:1px solid var(--fl-line);border-radius:var(--r-lg);
  overflow:hidden;
  transition:border-color .45s ease,transform .45s var(--ease-out),box-shadow .45s ease;
}
.pcard:hover{
  border-color:var(--fl-line-strong);transform:translateY(-4px);
  box-shadow:0 40px 80px -50px rgba(0,0,0,.9);
}
.pcard__media{position:relative;flex:0 0 42%;overflow:hidden}
.pcard__media img{
  display:block;width:100%;height:100%;object-fit:cover;object-position:center;
  transition:transform .7s var(--ease-out);
}
.pcard:hover .pcard__media img{transform:scale(1.05)}
.pcard__media--initials{
  flex:0 0 42%;display:flex;align-items:center;justify-content:center;
  background:linear-gradient(150deg,rgba(193,0,124,.28),rgba(0,115,255,.16) 60%,var(--surface-1));
  font-family:'Plus Jakarta Sans',Inter,ui-sans-serif,system-ui,sans-serif;font-weight:700;
  font-size:52px;letter-spacing:-.04em;color:rgba(255,255,255,.86);
}

.pcard__body{
  position:relative;z-index:1;flex:1;min-width:0;
  display:flex;flex-direction:column;justify-content:center;
  padding:30px 32px;
}
.pcard__name{margin:0;font-size:20px;font-weight:700;line-height:1.2;color:#fff;letter-spacing:-.016em}
.pcard__role{
  margin:8px 0 0;font-size:13px;font-weight:600;letter-spacing:.01em;
  background:linear-gradient(90deg,var(--pink),var(--fl-accent-mid,#ff4fb0) 42%,var(--blue));
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.pcard__text{
  margin:16px 0 0;padding-top:16px;border-top:1px solid var(--fl-line);
  font-size:14.5px;line-height:1.6;color:var(--t-70);
}

@media (max-width:1100px){ .pcards{grid-template-columns:repeat(2,1fr)} }
@media (max-width:720px){
  .pcards{grid-template-columns:1fr}
  .pcard{flex-direction:column;min-height:0}
  .pcard__media,.pcard__media--initials{flex:none;width:100%;height:240px}
  .pcard__body{padding:24px 22px}
}

/* ---------------- link up — full-bleed closing card ---------------- */
.linkup{
  position:relative;overflow:hidden;isolation:isolate;
  border-top:1px solid var(--fl-line);border-bottom:1px solid var(--fl-line);
  background:linear-gradient(160deg,rgba(193,0,124,.10) 0%,rgba(6,15,38,0) 46%),var(--navy-deep);
}
.linkup__canvas{
  position:absolute;inset:0;width:100%;height:100%;display:block;z-index:0;pointer-events:none;
  -webkit-mask-image:radial-gradient(105% 120% at 82% 55%, #000 8%, transparent 68%);
  mask-image:radial-gradient(105% 120% at 82% 55%, #000 8%, transparent 68%);
}
.linkup__inner{
  position:relative;z-index:1;
  /* The card column is exactly as wide as the card, so it can't drift around
     inside a fractional track. */
  display:grid;grid-template-columns:minmax(0,1fr) 340px;
  align-items:center;gap:clamp(40px,6vw,96px);
  padding:clamp(64px,8vw,120px) var(--bleed-gut);
}
/* Deliberately smaller than a page heading — this is a closing note, not a hero. */
.linkup__title{
  font-family:'Plus Jakarta Sans',Inter,ui-sans-serif,system-ui,sans-serif;font-weight:700;
  font-size:clamp(26px,2.9vw,40px);line-height:1.14;letter-spacing:-.022em;
  color:#fff;margin:0;
}
.linkup__title em{
  font-family:'Plus Jakarta Sans',Inter,ui-sans-serif,system-ui,sans-serif;font-weight:700;
  font-style:normal;color:var(--pink);
}
.linkup__lead{margin:26px 0 0;max-width:52ch;color:var(--t-70);font-size:18px;line-height:1.65}
.linkup__actions{display:flex;align-items:center;gap:28px;flex-wrap:wrap;margin-top:40px}

/* A face and a name on the plain background, nothing boxed around them —
   the surrounding section already carries the card feel, so a panel here
   just duplicated it. The photo carries more of the block now that
   there's no border/fill competing with it. */
.linkup__person{
  margin:0;justify-self:end;width:100%;max-width:340px;
  display:flex;align-items:center;gap:20px;
  padding:0;border:0;background:transparent;
}
.linkup__person img{
  width:104px;height:104px;flex:none;
  border-radius:var(--r-md);object-fit:cover;object-position:center top;
  transition:box-shadow .4s ease;
}
.linkup__person:hover img{box-shadow:0 0 0 2px rgba(193,0,124,.6)}
.linkup__person figcaption{display:flex;flex-direction:column;min-width:0}
.linkup__tag{
  font-family:var(--mono);font-size:10px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--blue);margin-bottom:6px;
}
.linkup__name{font-size:18px;font-weight:700;color:#fff;letter-spacing:-.012em;line-height:1.25}
.linkup__role{font-size:13.5px;color:var(--t-62);margin-top:3px;line-height:1.35}

@media (max-width:900px){
  .linkup__inner{grid-template-columns:1fr;gap:36px}
  .linkup__lead{max-width:none}
  .linkup__person{justify-self:start}
}
@media (max-width:720px){
  .linkup__actions{gap:20px;margin-top:32px}
  .linkup__person{gap:16px}
  .linkup__person img{width:76px;height:76px}
}

/* ---------------- motion primitives ---------------- */
.reveal--group{opacity:1;transform:none;transition:none}
/* The wipe lives on the inner media, never on the observed element itself:
   a clip-path on the target makes it invisible to IntersectionObserver, which
   would deadlock the reveal. */
.reveal--wipe img{
  clip-path:inset(0 0 100% 0);
  transition:clip-path 1.1s var(--ease-out) .12s;
}
.reveal--wipe.is-in img{clip-path:inset(0 0 0 0)}

[data-stagger] > *{
  opacity:0;transform:translateY(20px);
  transition:opacity .75s var(--ease-out),transform .75s var(--ease-out);
  transition-delay:calc(var(--i,0) * 80ms);
}
[data-stagger].is-in > *{opacity:1;transform:none}

/* masked word reveal for headings */
[data-split]{line-height:1.16}
.w{display:inline-block;overflow:hidden;vertical-align:top}
.wi{
  display:inline-block;transform:translateY(115%);opacity:0;
  transition:transform .95s var(--ease-out),opacity .6s ease;
  transition-delay:calc(var(--i) * 45ms);
}
[data-split].is-split-in .wi{transform:none;opacity:1}

[data-magnetic]{will-change:transform}
.btn--primary[data-magnetic]:hover{transform:none}

.btn--lg{padding:17px 34px;font-size:15px}

/* ---------------- responsive ---------------- */
@media (max-width:1100px){
  .build-row{grid-template-columns:76px minmax(0,1fr);gap:12px 28px}
  /* the index is a <p> too — only the description drops to the second row */
  .build-row p:not(.build-row__num){grid-column:2}
}
@media (max-width:720px){
  .tech-panel{padding:38px 24px}
  .build-row{grid-template-columns:1fr;gap:10px;padding:26px 8px}
  .build-row p:not(.build-row__num){grid-column:1}
  .build-row:not(:has(.build-row__num)){grid-template-columns:1fr}
  .build-row__link{grid-column:1}
}

/* ---------------- reduced motion ---------------- */
@media (prefers-reduced-motion:reduce){
  [data-stagger] > *{opacity:1;transform:none;transition:none}
  .reveal--wipe img{clip-path:none;transition:none}
  .wi{opacity:1;transform:none;transition:none}
  .scroll-progress{display:none}
  .grid-spot.is-inview::before{animation:none}
}
