/* ============================================================
   Frontal GTM API directory
   Design system sampled from Frontal's 2026 rebuild ("grafit") on
   2026-10-13, and written up in ../frontal-brand.md.

   One dark theme throughout. Their finished surfaces (home, service
   pages, blog index) are all near-black; the light article template
   is their unfinished legacy blog and is deliberately not copied.

   The look is carried by hairlines, not by cards and shadows:
   - radius 0 by default, and nothing above 8px except a pill;
   - grids are cells divided by 1px rules, with no fill;
   - the shell is framed by a vertical hairline down each side;
   - Season Serif 400 for display, Inter for body, JetBrains Mono
     for every uppercase label.
   Do not invent colours: every value below is theirs.
   ============================================================ */

/* Self-hosted, taken from their build. None of the three is on Google
   Fonts, and Season Serif ships one weight, which is why display type
   is 400 rather than bold. */
@font-face{font-family:'Season Serif';src:url('/fonts/grafit/season-serif-regular.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Inter';src:url('/fonts/grafit/inter-latin-subset.woff2') format('woff2');font-weight:100 900;font-style:normal;font-display:swap}
@font-face{font-family:'JetBrains Mono';src:url('/fonts/grafit/jetbrains-mono-latin-subset.woff2') format('woff2');font-weight:400;font-style:normal;font-display:swap}

:root{
  color-scheme: dark;

  /* grounds */
  --bone:#04060C;          /* their --gf-bg, the page ground */
  --paper:rgba(255,255,255,.055);  /* table heads, tags, hover fills */
  --card:rgba(255,255,255,.028);   /* cell and card ground */
  --panel:#111314;         /* the one step up from the ground, for inset panels */
  --sand:rgba(255,255,255,.12);
  --sand-2:rgba(255,255,255,.07);

  --ink:#FFFFFF;           /* headings */
  --ink-2:rgba(255,255,255,.64);   /* their --gf-muted, body text */
  /* Raised to WCAG AA on 2026-10-19: .45 measured 4.46:1 and .35 3.08:1 on the
     ground, under the 4.5:1 small text needs. .56 is 6.4:1 and .5 is 5.3:1. */
  --muted:rgba(255,255,255,.56);   /* secondary text */
  --faint:rgba(255,255,255,.5);    /* labels, counts */

  --line:rgba(255,255,255,.12);    /* their --gf-line */
  --line-soft:rgba(255,255,255,.07);
  --line-cell:rgba(255,255,255,.15); /* grid cell dividers */

  --accent:#A5CCFD;        /* their nav blue, the accent as used */
  --accent-ink:#A5CCFD;    /* same on dark; there is no second accent */
  --accent-raw:#9EC9FF;    /* their --gf-blue */
  --accent-soft:rgba(165,204,253,.12);

  --mint:rgba(165,204,253,.16);
  --mint-deep:#A5CCFD;
  --rose:rgba(255,255,255,.1);
  --rose-deep:rgba(255,255,255,.55);

  /* the ground, named for the rules that still call it a console */
  --console:#04060C;
  --console-2:#111314;

  /* The opaque surface for anything that FLOATS over the page: the facts
     dialog, the expanded TOC panel, the nav dropdown. --card and --paper are
     deliberately translucent, which is right for a cell sitting on the ground
     and wrong for a panel sitting on top of other content: the facts dialog
     used --card (2.8% white) and the page read straight through it, and the
     expanded TOC had no background at all so the factsheet showed through the
     panel covering it. Anything with position:absolute, position:fixed or a
     z-index that lifts it over content takes this. */
  --surface:#04060C;       /* the site's dropdown ground, with a #ffffff26 hairline */
  --cream:#FFFFFF;
  --grey:#9B9CA3;          /* footer and caption text */
  --dim:rgba(255,255,255,.5);
  --cyan:#9EC9FF;
  --emerald:#A5CCFD;
  --glow:0 0 0 1px rgba(165,204,253,.28);

  /* Radius 0 is the default. Nothing here is above 8px except a pill. */
  --r-sm:2px; --r:0px; --r-lg:0px; --r-xl:0px;
  --shadow:none;
  --shadow-lg:none;

  --wrap:1280px;           /* their --gf-shell */
  --gut:clamp(20px, 5.5vw, 56px);

  --sans:'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --display:'Season Serif', Georgia, 'Times New Roman', serif;
  --mono:'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* GRAFIT's type scale (grafit.css): every size on the page is one of these, and
     nothing reads under 12px on a phone. Headings step at 1001px rather than
     scaling with the window, so a size between two tokens never renders. */
  --t-11:11px;
  --t-display:40px; --t-h1:40px; --t-h2:32px; --t-h2-sm:28px; --t-title:18px; --t-lede:16px;

  /* the scanline band that separates their major sections */
  --scan:repeating-linear-gradient(to bottom, rgba(255,255,255,.1) 0 1px, transparent 1px 4px);
}

@media (min-width:1001px){
  :root{--t-display:56px; --t-h1:48px; --t-h2:40px; --t-h2-sm:32px; --t-title:20px; --t-lede:18px}
}
@media (max-width:700px){ :root{--t-11:12px} }

*,*::before,*::after{box-sizing:border-box}
/* JetBrains Mono and Season Serif ship one weight each: never fake a bold */
html{font-synthesis-weight:none}

body{
  margin:0;
  font-family:var(--sans);
  font-size:16px;
  line-height:1.6;
  letter-spacing:-.015em;   /* GRAFIT's body tracking */
  color:var(--ink-2);
  background-color:var(--bone);
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}

img{max-width:100%;display:block}
a{color:inherit}

/* ---------- layout ----------
   Their .gf-shell: a 1280px column framed by a hairline down each side, so
   the whole page reads as one ruled grid rather than a stack of bands. The
   lines are drawn on a wrapper rather than with a border, because a border
   would sit inside the gutter and break at every band boundary. */
.wrap,.wrap-narrow{
  max-width:var(--wrap);margin:0 auto;padding-inline:var(--gut);
  position:relative;
}
.wrap::before,.wrap::after,.wrap-narrow::before,.wrap-narrow::after{
  content:"";position:absolute;top:0;bottom:0;width:1px;
  background:var(--line);pointer-events:none;z-index:0;
}
.wrap::before,.wrap-narrow::before{left:0}
.wrap::after,.wrap-narrow::after{right:0}
.wrap > *,.wrap-narrow > *{position:relative;z-index:1}
/* Text-heavy bands run as two columns: the section header holds the left
   rail, the content fills the right. Same outer container as every other
   band, and no dead gutter on the right. */
.wrap-narrow{
  display:grid;grid-template-columns:minmax(0,326px) minmax(0,1fr);
  gap:clamp(26px,4.6vw,84px);align-items:start;
}
.wrap-narrow > .col-head{position:sticky;top:100px}
.wrap-narrow > .col-head .lede{font-size:16px;line-height:1.58;max-width:38ch}
.wrap-narrow > .col-head .eyebrow{margin-bottom:.85rem}
.wrap-narrow > .col-head h2.sec{margin-bottom:.7rem}
.wrap-narrow > .col-body{min-width:0}
.col-head-cta{margin-top:1.3rem;display:flex;flex-wrap:wrap;gap:.5rem}
section{position:relative}
.band{padding-block:clamp(56px,7vw,104px)}
/* One ground throughout, so a "paper" band is a barely-lifted panel rather
   than a second colour, separated by their scanline rule. */
.band--paper{background:rgba(255,255,255,.022);border-block:1px solid var(--line-soft)}
.band--tight{padding-block:clamp(40px,5vw,68px)}
/* their section separator: a 54px scanline that fades out downward */
.band--scan{position:relative}
.band--scan::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:54px;
  background:var(--scan);
  -webkit-mask-image:linear-gradient(transparent,#000);mask-image:linear-gradient(transparent,#000);
  pointer-events:none;
}

/* ---------- type ---------- */
h1,h2,h3,h4{color:var(--ink);margin:0;text-wrap:balance}

/* Frontal sets display type in Season Serif at 400 with -0.03em tracking.
   400 is not a choice: it is the only weight the face ships. An <em> is the
   accent phrase ("GTM systems, in the open"), never italic. */
.display{
  font-family:var(--display);
  font-weight:400;
  letter-spacing:-0.03em;
  line-height:1.0;
  font-size:var(--t-display);
  color:var(--ink);
}
.display em{font-style:normal;color:var(--accent);font-weight:400}

h2.sec{
  font-family:var(--display);
  font-weight:400;
  letter-spacing:-0.03em;
  line-height:1.04;
  font-size:var(--t-h2);
  margin:0 0 .5rem;
}
h2.sec em{font-style:normal;color:var(--accent)}

/* Their h3 steps out of the serif and back into Inter at 500 to 600: a
   subhead is UI, not display. */
h3.sub{
  font-family:var(--sans);
  font-weight:500;
  font-size:var(--t-title);
  letter-spacing:-0.015em;
  line-height:1.25;
  margin:0 0 .4rem;
}

.lede{
  font-size:var(--t-lede);
  line-height:1.6;
  color:var(--ink-2);
  max-width:62ch;
  margin:0;
}
.note{font-size:14px;line-height:1.58;color:var(--muted)}
.tiny{font-size:12px;line-height:1.5;color:var(--muted)}

p{margin:0 0 1rem}
p:last-child{margin-bottom:0}

/* Their .gf-eyebrow: font:400 var(--t-11)/1.4 mono, uppercase, .04em (measured on the
   site at 858fbf9: 11px with .44px, 12px with .48px on a phone). The old
   brand's [ BRACKETS ] are gone; the new device is a 6px corner tick at
   each of the four corners, which .eyebrow--tick adds. */
.eyebrow{
  display:inline-flex;align-items:center;gap:.55em;
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;letter-spacing:.04em;text-transform:uppercase;
  line-height:1.4;color:var(--accent);
  margin-bottom:1.1rem;
}
.eyebrow--plain{color:rgba(255,255,255,.64)}
/* the corner-tick bracket, drawn as four 6px corner gradients */
/* the site's bracket eyebrow (.gf-eyebrow.gf-bracket-link, one rule since ffcdf5e): 12px at
   .015em, 4px by 14px, 24px tall, the ticks at 70% */
.eyebrow--tick{padding:4px 14px;min-height:24px;font-size:12px;letter-spacing:.015em;position:relative}
.eyebrow--tick::before,.eyebrow--tick::after{
  content:"";position:absolute;inset:0;pointer-events:none;opacity:.7;
}
.eyebrow--tick::before{
  background:
    linear-gradient(currentColor,currentColor) 0 0/6px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 0/6px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 0 100%/6px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 100%/6px 1px no-repeat;
}
.eyebrow--tick::after{
  background:
    linear-gradient(currentColor,currentColor) 0 0/1px 6px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 0/1px 6px no-repeat,
    linear-gradient(currentColor,currentColor) 0 100%/1px 6px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 100%/1px 6px no-repeat;
}

.rule-head{display:flex;align-items:center;gap:1rem;margin-bottom:1.6rem}
.rule-head .line{flex:1;height:1px;background:var(--line)}
.rule-head .count{font-family:var(--mono);font-size:12px;color:var(--muted);white-space:nowrap}

/* ---------- hero ---------- */
.hero{position:relative;overflow:hidden;padding-block:clamp(38px,5vw,74px) clamp(48px,6vw,86px)}
/* their hero bloom: a cool blue wash, not the old orange */
.hero::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(760px 420px at 50% -10%, rgba(79,128,198,.16), transparent 68%);
}
.hero > *{position:relative;z-index:1}
.hero-grid{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:clamp(28px,4vw,60px);align-items:center}
.hero-copy .lede{margin-top:1.15rem}
.hero-actions{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:1.8rem}

/* Buttons are the site's own (.gf-button, in the "Frontal site chrome" section at
   the end of this file): the white primary that sweeps light blue on hover, and the
   translucent secondary with corner brackets. gd-btn is only the directory's layout
   hook. It is not called .btn, which is the retired frontal-v2.css button on the
   site and fails the site's brand-components test. How buttons sit in a row (.gd-btns,
   the hero pairs, the page CTA) is the "Buttons in rows" block at the end of this file. */
.gd-btn{gap:.5rem}
.hero-actions .gd-btn{flex:none}

/* ---------- console panel ---------- */
/* Frontal's product panels: near-black, a thin mono title bar, orange for the
   live element. The code palette uses only their named colours. */
.console{
  background:var(--console);
  border-radius:var(--r-lg);
  box-shadow:var(--shadow-lg);
  overflow:hidden;
  font-family:var(--mono);
  border:1px solid rgba(255,255,255,.1);
}
.console-bar{
  display:flex;align-items:center;gap:.55rem;
  padding:.7rem 1rem;border-bottom:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.03);
}
.console-bar .dots{display:flex;gap:.33rem}
.console-bar .dots i{width:8px;height:8px;border-radius:2px;display:block;background:rgba(255,255,255,.16)}
.console-bar .dots i:nth-child(1){background:var(--accent)}
.console-bar .name{font-size:var(--t-11);color:var(--dim);letter-spacing:.04em;margin-left:.3rem}
.console-bar .tag{margin-left:auto;font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent);font-family:var(--mono);font-weight:400}
.console-body{padding:1.1rem 1.15rem;font-size:12px;line-height:1.9;color:rgba(255,255,255,.82);overflow-x:auto}
.console-body pre{margin:0;font:inherit;white-space:pre}
.c-key{color:var(--cyan)}.c-str{color:rgba(255,255,255,.92)}.c-num{color:var(--cream)}.c-com{color:#8B8C94}
.c-meth{color:var(--accent);font-weight:400}.c-punc{color:var(--dim)}

.console-foot{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  padding:.8rem 1.15rem;border-top:1px solid rgba(255,255,255,.08);
  background:rgba(255,255,255,.025);
}
.console-foot .lbl{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--dim);font-weight:400}
.console-foot .val{font-family:var(--mono);font-size:12px;color:var(--cream);font-weight:400}
.status-200{
  display:inline-flex;align-items:center;gap:.35rem;
  background:rgba(165,204,253,.14);color:var(--emerald);
  border:1px solid rgba(165,204,253,.32);
  border-radius:0;padding:.14rem .55rem;
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;letter-spacing:.04em;
}
.kv-rows{display:grid;gap:.3rem;padding:.85rem 1.15rem 1rem}
.kv-row{
  display:flex;justify-content:space-between;gap:1rem;align-items:center;
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.07);
  border-radius:var(--r-sm);padding:.46rem .7rem;
  font-family:var(--mono);font-size:12px;color:var(--grey);
}
.kv-row b{color:var(--cream);font-weight:400}

/* ---------- stat strip ---------- */
.stats{
  display:grid;grid-template-columns:repeat(4,1fr);
  border:1px solid var(--line);border-radius:var(--r-lg);
  background:var(--card);overflow:hidden;margin-top:clamp(32px,4vw,52px);
}
.stat{padding:1.35rem 1.5rem;border-right:1px solid var(--line-soft)}
.stat:last-child{border-right:0}
.stat b{display:block;font-family:var(--display);font-size:var(--t-h2);font-weight:400;color:var(--ink);line-height:1;letter-spacing:-.04em}
/* The child selector matters: a stat value can be wrapped in
   <span data-count> for counts.py, and the bare descendant rule turned those
   nine values into .62rem mono labels instead of display numbers. */
.stat > span{display:block;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-top:.6rem;line-height:1.5}

/* ---------- directory shell ---------- */
.dir{display:grid;grid-template-columns:252px minmax(0,1fr);gap:clamp(20px,2.6vw,40px);align-items:start}
.filters{
  position:sticky;top:92px;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:1.15rem 1.15rem 1.3rem;box-shadow:var(--shadow);
  max-height:calc(100vh - 118px);overflow-y:auto;
}
.filters h3{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--faint);font-weight:400;margin:0 0 .2rem}
.fgroup{border-top:1px solid var(--line-soft);padding-top:.95rem;margin-top:.95rem}
.fgroup:first-of-type{border-top:0;margin-top:.75rem;padding-top:0}
.fgroup > .flabel{
  display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  font-size:14px;letter-spacing:-.005em;
  color:var(--ink);font-weight:500;margin-bottom:.55rem;
}
.fgroup .hint{font-size:12px;color:var(--faint);text-transform:none;letter-spacing:0;font-weight:500}
.fopt{display:flex;align-items:flex-start;gap:.52rem;padding:.24rem 0;cursor:pointer;font-size:14px;color:var(--ink-2);line-height:1.38}
.fopt input{
  appearance:none;width:15px;height:15px;flex:none;margin-top:.19rem;
  border:1.5px solid var(--line);border-radius:2px;background:var(--paper);
  cursor:pointer;transition:.14s;position:relative;
}
.fopt input:checked{background:var(--accent);border-color:var(--accent)}
.fopt input:checked::after{content:"";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--card);border-width:0 1.6px 1.6px 0;transform:rotate(42deg)}
.fopt:hover{color:var(--ink)}
.fopt .n{margin-left:auto;font-family:var(--mono);font-size:var(--t-11);color:var(--faint);padding-left:.4rem}
.fopt .txt{flex:1;min-width:0}
.fopt .sub{display:block;font-size:12px;color:var(--faint);line-height:1.35}
.clearall{
  width:100%;margin-top:1rem;padding:.5rem;border:1px solid var(--line);
  background:transparent;border-radius:var(--r-sm);font-family:var(--sans);
  font-size:13px;font-weight:500;color:var(--muted);cursor:pointer;transition:.15s;
}
/* hover: see the Final QA block at the end, mouse only */

.dir-bar{display:flex;align-items:center;gap:.8rem;flex-wrap:wrap;margin-bottom:1.1rem}
.search{
  flex:1;min-width:200px;display:flex;align-items:center;gap:.55rem;
  background:var(--card);border:1px solid var(--line);border-radius:2px;padding:.6rem 1rem;
}
.search:focus-within{border-color:rgba(165,204,253,.6);box-shadow:0 0 0 1px rgba(165,204,253,.6),0 0 0 4px rgba(165,204,253,.1)}
.search input{border:0;background:transparent;outline:0;flex:1;font-family:var(--sans);font-size:15px;color:var(--ink);min-width:0}
.search input::placeholder{color:var(--faint)}
.search svg{flex:none;color:var(--faint)}
.showing{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;color:var(--muted);white-space:nowrap}
.sortsel{
  border:1px solid var(--line);background:var(--card);border-radius:2px;
  padding:.58rem .9rem;font-family:var(--sans);font-size:14px;color:var(--ink);cursor:pointer;
}

/* ---------- api card ---------- */
/* min(Npx,100%) in every auto-fill track below, not a bare Npx: a bare minimum
   wider than the container makes one track that overhangs it, which put the
   cards 8px past the right edge of the page at 400px. */
/* Their grids are cells, not cards: no gap, no radius, no fill, and one
   shared hairline between neighbours. The container draws the top and left
   edge, each cell draws its own right and bottom, so a part-full last row
   still closes correctly. */
.cards{
  display:grid;gap:0;grid-template-columns:repeat(auto-fill,minmax(min(372px,100%),1fr));
  align-items:stretch;border-top:1px solid var(--line-cell);border-left:1px solid var(--line-cell);
}
.card{
  background:transparent;border:0;border-right:1px solid var(--line-cell);
  border-bottom:1px solid var(--line-cell);border-radius:0;
  padding:1.45rem 1.5rem;transition:background .18s;
}
.card:hover{background:rgba(255,255,255,.028)}
.card.is-featured{background:rgba(165,204,253,.06)}
.card-top{display:flex;align-items:flex-start;gap:.8rem;margin-bottom:.85rem}

/* monogram marker: stands in for a vendor logo, no external request */
.mk{
  display:inline-grid;place-items:center;flex:none;
  width:22px;height:22px;border-radius:2px;
  background:var(--sand-2);border:1px solid var(--line);
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;
  letter-spacing:.02em;color:var(--ink);
  padding:0;line-height:1;
}
.mk-lg{width:34px;height:34px;border-radius:2px;font-size:12px}
.card.is-featured .mk-lg{background:var(--accent-soft);border-color:rgba(165,204,253,.35);color:var(--accent-ink)}
.panel h3 .mk{margin-right:.1rem}
.card-title{flex:1;min-width:0}
.card-title h3{font-family:var(--sans);font-size:18px;font-weight:500;letter-spacing:-.02em;margin:0;line-height:1.28}
.card-title h3 a{text-decoration:none;color:var(--ink)}
.card-title h3 a:hover{color:var(--accent-ink)}
/* "Best for" sits with the name, as a subtitle */
.card .best{
  display:block;font-size:13px;font-weight:500;color:var(--accent-ink);
  line-height:1.35;margin-top:.18rem;
}
.card-top .flag{margin-left:auto;flex:none}

/* Two separate rows: what it does, then what it is */
.card-tags{display:grid;gap:.35rem;margin-bottom:.8rem}
.tag-row{display:flex;flex-wrap:wrap;gap:.35rem;align-items:center}
.tag-row .rl{
  font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;
  color:var(--faint);font-weight:400;margin-right:.1rem;min-width:56px;
}

.card p.blurb{font-size:15px;line-height:1.56;color:var(--ink-2);margin:0 0 .8rem}
.card-bullets{list-style:none;margin:0 0 .95rem;padding:0;display:grid;gap:.3rem}
.card-bullets li{font-size:13px;color:var(--muted);padding-left:1.05rem;position:relative;line-height:1.48}
.card-bullets li::before{content:"";position:absolute;left:0;top:.53em;width:5px;height:5px;border-radius:0;background:var(--sand)}

/* The foot is one unit across the card's content box: a hairline, a ledger line (the
   label on the left, the figure on the right, as the site's play cards set their meta
   line), then the two buttons as one equal-width row (.gd-btns, at the end of this
   file) whose fills land on the same two edges. The ledger sits with the buttons, not
   between them and the hairline: 24px above it, 8px under it (plus the fill's own 4px
   inset), so the figure is about 18px from the fill and 31px from the rule, and a focused
   button's ring (6px outside its target) still clears the figure. */
.card-foot{
  display:flex;flex-direction:column;gap:8px;container-type:inline-size;
  padding-top:24px;border-top:1px solid var(--line-soft);margin-top:auto;
}
.price{font-family:var(--mono);font-size:13px;color:var(--ink);font-weight:400}
.price small{color:var(--muted);font-weight:400}
/* Where the buttons stack (a foot under 304px, the width at which two columns stop holding
   "Read the review" with 12px either side of the fill), the ledger stacks too: the label
   over its figure, left-aligned, the way the factsheet sets a label over its value. Wider,
   a figure too long to sit beside its label (an MCP endpoint) drops under it the same way. */
.card-meta{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:2px 16px;margin:0;min-height:22px}
.card-meta .rl{flex:none;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--faint)}
.card-meta > :last-child{min-width:0;overflow-wrap:anywhere}
/* an MCP card's prose value (a server with no public URL) is the figure of its ledger, so it
   carries the figure's colour; it stays Inter because it is a sentence, not data, and stays
   12px because at 13px "Two servers: remote CRM, local developer CLI" no longer fits beside
   its label in a 340px foot and pushes its card's foot 17px out of line with its row */
.card-meta .tiny{color:var(--ink)}
@container (max-width:303.98px){.card-meta{display:grid;justify-content:start}}
/* The slot above the buttons holds either a mono endpoint or a line of prose.
   Both take the same line height, or the taller one pushes its foot 2px out of
   line with the card beside it. */
.card-foot .price,.card-foot .tiny{line-height:1.65;min-height:1.65em}
.card{display:flex;flex-direction:column}

/* ---------- pills ----------
   Their tag is a square mono chip, as on the blog index filter row. The new
   palette is white, greys and one blue: there is no green and no red on any
   of their surfaces, so the semantic chips carry weight instead of hue.
   Free and MCP (the two things a reader scans for) get the blue; everything
   else is a grey chip. */
.pill{
  display:inline-flex;align-items:center;gap:.36rem;
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;letter-spacing:.04em;text-transform:uppercase;
  line-height:1.5;padding:.26rem .6rem;border-radius:0;
  border:1px solid var(--line);background:transparent;color:var(--muted);
  white-space:nowrap;
}
.pill--stage{background:rgba(255,255,255,.05);border-color:var(--line);color:rgba(255,255,255,.82)}
.pill--accent{background:var(--accent-soft);border-color:rgba(165,204,253,.3);color:var(--accent)}
.pill--mcp{background:rgba(165,204,253,.1);border-color:rgba(165,204,253,.32);color:var(--accent)}
.pill--mcp::before{content:"";width:5px;height:5px;border-radius:0;background:var(--accent)}
.pill--free{background:var(--accent);border-color:var(--accent);color:#04060C;font-weight:400}
.pill--ink{background:#FFFFFF;border-color:#FFFFFF;color:#04060C}
.pill--warn{background:transparent;border-color:var(--line);color:var(--muted)}

/* ---------- tables ---------- */
.table-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--card)}
table.cmp{border-collapse:collapse;width:100%;min-width:780px;font-size:14px}
/* The 780px floor is for a comparison table, where two vendors sit side by side
   in a full-width column and squeezing them is worse than scrolling. A product
   page reuses .cmp for two and three column prose tables inside .prod-main,
   which is 707px at 1440, so the floor bound there instead and clipped the last
   column on all 23 review pages: "15,000 calls a month" rendered as "15,000
   calls". verify-layout.py could not see it, because section 6 tells it to
   ignore anything clipped by a scrollable ancestor, and that rule is right.
   Only above 1120px, where .prod-side is still a sidebar. Below that the article
   goes full width and the floor stops binding on its own, and forcing min-width:0
   all the way down was worse than the bug: at 400px the columns squeeze until
   .mono's overflow-wrap:anywhere breaks a price mid-number, "$179" as "$17 9".
   Measured 2026-10-08. */
@media (min-width:1121px){ .prod-main table.cmp{min-width:0} }
table.cmp th,table.cmp td{padding:.78rem .95rem;text-align:left;border-bottom:1px solid var(--line-soft);vertical-align:top}
table.cmp thead th{
  background:var(--paper);font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);font-weight:400;border-bottom:1px solid var(--line);
}
table.cmp tbody th{font-weight:500;color:var(--ink);width:190px;background:var(--card)}
table.cmp tr:last-child td,table.cmp tr:last-child th{border-bottom:0}
table.cmp tbody tr:hover td,table.cmp tbody tr:hover th{background:var(--paper)}
table.cmp .v-name{display:flex;align-items:center;gap:.5rem;font-weight:400;color:var(--ink);text-decoration:none;font-size:15px}
table.cmp .v-name:hover{color:var(--accent-ink)}
.yes{color:var(--mint-deep);font-weight:500}
.no{color:var(--faint)}
/* A base URI or an endpoint path in prose is one unbreakable token, and a long
   one pushed the whole Salesforce page sideways at 400px. anywhere breaks only
   when the token would otherwise overflow, so normal mono spans are untouched. */
.mono{font-family:var(--mono);font-size:12px;overflow-wrap:anywhere}

/* ---------- numbered guide ---------- */
.guide{display:grid;gap:0}
.guide-item{
  display:grid;grid-template-columns:64px minmax(0,1fr);gap:clamp(14px,2vw,28px);
  padding:1.6rem 0;border-top:1px solid var(--line);
}
.guide-item:first-child{border-top:0;padding-top:.3rem}
.guide-num{font-family:var(--mono);font-size:12px;font-weight:400;color:var(--accent);padding-top:.3rem;letter-spacing:.04em}
.guide-item h3{font-family:var(--display);font-weight:400;font-size:20px;letter-spacing:-.02em;margin:0 0 .5rem;line-height:1.24}

/* ---------- tiles ---------- */
.tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(268px,100%),1fr));gap:0;border-top:1px solid var(--line-cell);border-left:1px solid var(--line-cell)}
.tile{
  background:transparent;border:0;border-right:1px solid var(--line-cell);border-bottom:1px solid var(--line-cell);border-radius:0;
  padding:1.45rem 1.5rem;text-decoration:none;display:flex;flex-direction:column;gap:.55rem;
  transition:border-color .18s,transform .18s,box-shadow .18s;
}
.tile:hover{background:rgba(255,255,255,.028)}
.tile-head{display:flex;align-items:center;justify-content:space-between;gap:.7rem}
.tile-head h3{font-family:var(--sans);font-size:16px;font-weight:500;letter-spacing:-.02em;margin:0}
.tile .cnt{font-family:var(--mono);font-size:var(--t-11);color:var(--accent-ink);background:var(--accent-soft);border-radius:0;padding:.16rem .52rem;flex:none}
.tile p{font-size:14px;color:var(--muted);line-height:1.52;margin:0}
.tile .go{font-size:13px;font-weight:500;color:var(--ink);margin-top:auto;padding-top:.35rem;display:inline-flex;align-items:center;gap:.3rem}
.tile:hover .go{color:var(--accent-ink)}

/* ---------- quotes ---------- */
.quotes{columns:3;column-gap:.85rem}
.quote{
  break-inside:avoid;background:var(--card);border:1px solid var(--line);
  border-radius:var(--r);padding:1rem 1.1rem;margin-bottom:.85rem;
  display:block;text-decoration:none;transition:border-color .16s;
}
.quote:hover{background:rgba(255,255,255,.028)}
.quote .qtag{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);font-weight:400}
.quote blockquote{margin:.45rem 0 .6rem;font-size:14px;line-height:1.56;color:var(--ink-2)}
.quote cite{font-style:normal;font-size:var(--t-11);color:var(--muted);font-family:var(--mono)}

/* ---------- FAQ ----------
   The site's FAQ rows (FAQ.astro): a faint card per question that lifts when open, a
   hairline between, corner ticks on top of the list, the question at 400, and a plus
   drawn in two 1px rules that turns into a minus. */
.faq{position:relative;border-top:0}
.faq::before,.faq::after{content:"";position:absolute;top:0;width:6px;height:6px;border-top:1px solid rgba(255,255,255,.53);pointer-events:none;z-index:1}
.faq::before{left:0;border-left:1px solid rgba(255,255,255,.53)}
.faq::after{right:0;border-right:1px solid rgba(255,255,255,.53)}
.faq details{border-bottom:1px solid #ffffff0d;background:rgba(255,255,255,.025)}
.faq details[open]{background:rgba(255,255,255,.045)}
.faq summary{list-style:none;cursor:pointer;padding:23px 56px 23px 24px;position:relative;font-weight:400;font-size:15px;line-height:1.4;letter-spacing:-.015em;color:var(--ink)}
.faq summary::-webkit-details-marker{display:none}
.faq summary::before,.faq summary::after{
  content:"";position:absolute;right:24px;top:50%;width:10px;height:1px;background:#fff;opacity:.6;
}
.faq summary::after{transform:rotate(90deg)}
.faq details[open] summary::after{transform:rotate(0deg)}
@media (prefers-reduced-motion:no-preference){.faq summary::after{transition:transform 300ms var(--gf-ease-ui)}}
.faq .ans{padding:0 24px 24px;font-size:14px;line-height:1.5;color:#A5A6AC}
.faq .ans p{margin:0 0 .7rem}
.faq .ans a{color:var(--accent-ink)}
@media (max-width:700px){.faq summary{padding:21px 48px 21px 16px;font-size:16px}.faq summary::before,.faq summary::after{right:16px}.faq .ans{padding:0 16px 22px;font-size:16px}}

/* ---------- scorecard ---------- */
.score{display:grid;gap:0}
.score-row{display:grid;grid-template-columns:160px 104px 48px minmax(0,1fr);gap:1rem;align-items:center;padding:.68rem 0;border-bottom:1px solid var(--line-soft)}
.score-row:last-child{border-bottom:0}
.score-row .dim{font-weight:500;color:var(--ink);font-size:15px}
.score-bar{display:flex;gap:3px}
.score-bar i{width:100%;height:7px;border-radius:2px;background:var(--sand-2);display:block}
.score-bar i.on{background:var(--accent)}
.score-row .why{font-size:14px;color:var(--muted);line-height:1.5}
.score-row .val{font-family:var(--mono);font-size:12px;color:var(--ink);font-weight:400}

/* ---------- pricing tiers ---------- */
.tiers{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:0;border-top:1px solid var(--line-cell);border-left:1px solid var(--line-cell)}
.tier{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:1.25rem 1.3rem;display:flex;flex-direction:column}
.tier.best{border-color:var(--accent);background:linear-gradient(180deg,rgba(165,204,253,.09),var(--card) 50%)}
.tier h3{font-family:var(--sans);font-size:16px;font-weight:500;margin:0 0 .5rem}
.tier .amt{font-family:var(--display);font-size:28px;font-weight:400;color:var(--ink);letter-spacing:-.04em;line-height:1}
.tier .per{font-size:13px;color:var(--muted);margin-top:.3rem;margin-bottom:.9rem;display:block}
.tier ul{list-style:none;margin:0;padding:0;display:grid;gap:.42rem}
.tier li{font-size:14px;color:var(--ink-2);padding-left:1.05rem;position:relative;line-height:1.45}
.tier li::before{content:"";position:absolute;left:0;top:.5em;width:5px;height:5px;border-radius:0;background:var(--accent)}

/* ---------- prose / callout ---------- */
.prose h3{font-family:var(--display);font-weight:400;font-size:20px;letter-spacing:-.02em;margin:2rem 0 .55rem}
.prose h3:first-child{margin-top:0}
.prose ul{margin:0 0 1.1rem;padding-left:1.1rem}
.prose li{margin-bottom:.42rem;line-height:1.58}
.prose a{color:var(--accent-ink);text-decoration:underline;text-underline-offset:2px;text-decoration-thickness:1px}

.callout{
  background:var(--card);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:var(--r);padding:1.05rem 1.25rem;margin:1.3rem 0;
}
.callout .lbl{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);font-weight:400;display:block;margin-bottom:.4rem}
.callout p{font-size:15px;line-height:1.58;margin:0}
/* a command to copy: monospaced, wraps rather than scrolls on a phone */
.callout p.snippet{
  font-family:var(--mono);font-size:12px;line-height:1.75;color:var(--ink);
  white-space:pre-wrap;word-break:break-word;
}

.split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(290px,100%),1fr));gap:1.1rem}
.panel{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:1.3rem 1.4rem}
.panel h3{font-family:var(--sans);font-size:16px;font-weight:500;margin:0 0 .75rem;display:flex;align-items:center;gap:.45rem}
.panel ul{list-style:none;margin:0;padding:0;display:grid;gap:.55rem}
.panel li{font-size:14px;line-height:1.52;color:var(--ink-2);padding-left:1.35rem;position:relative}
.panel--good li::before{content:"+";position:absolute;left:0;top:-.05em;color:var(--mint-deep);font-weight:500}
.panel--bad li::before{content:"";position:absolute;left:.1em;top:.78em;width:.55em;height:1px;background:var(--rose-deep)}

/* ---------- stack flow ---------- */
.stack-flow{display:grid;gap:.55rem}
.step{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:1.1rem;align-items:center;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:1.05rem 1.25rem;
}
.step-n{
  width:30px;height:30px;border-radius:2px;background:var(--console);color:var(--cream);
  display:grid;place-items:center;font-family:var(--mono);font-size:var(--t-11);font-weight:400;flex:none;
}
.step.is-alt .step-n{background:var(--accent);color:#04060C}
.step h3{font-family:var(--sans);font-size:16px;font-weight:500;margin:0 0 .2rem}
.step p{font-size:14px;color:var(--muted);margin:0;line-height:1.5}
.step .cost{font-family:var(--mono);font-size:12px;color:var(--ink);font-weight:400;text-align:right;white-space:nowrap}
.step .cost small{display:block;font-size:12px;color:var(--faint);font-weight:400;font-family:var(--sans)}
.step-join{display:grid;place-items:center;color:var(--muted);min-height:12px;font-size:13px}

/* ---------- breadcrumb ---------- */
.crumb{display:flex;gap:.55rem;align-items:center;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:1.1rem;flex-wrap:wrap}
.crumb a{text-decoration:none;color:var(--muted)}
.crumb a:hover{color:var(--accent-ink)}
.crumb span{color:var(--faint)}

/* ---------- CTA ----------
   Dark surfaces share Frontal's homepage treatment: a faint 32px grid that
   fades out, and an orange bloom where the call button sits. */
.cta{background:var(--console);color:var(--grey);border-radius:var(--r-xl);padding:clamp(30px,4.5vw,56px);position:relative;overflow:hidden;border:1px solid rgba(255,255,255,.08)}
.cta::before{
  content:"";position:absolute;inset:0;
  background-image:linear-gradient(rgba(255,255,255,.04) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.04) 1px, transparent 1px);
  background-size:32px 32px;
  -webkit-mask-image:radial-gradient(ellipse 70% 90% at 88% 18%, #000, transparent 72%);
  mask-image:radial-gradient(ellipse 70% 90% at 88% 18%, #000, transparent 72%);
}
.cta::after{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(520px circle at 92% 110%, rgba(165,204,253,.2), transparent 70%)}
.cta > *{position:relative;z-index:1}
.cta h2{color:#FFFFFF;font-family:var(--display);font-weight:400;font-size:var(--t-h2);letter-spacing:-.03em;line-height:1.08;margin:0 0 .75rem}
.cta h2 em{font-style:normal;color:var(--accent)}
.cta p{max-width:58ch;font-size:16px;line-height:1.62;color:var(--grey)}
.cta .cta-promise{display:flex;align-items:center;gap:.5rem;margin:1.1rem 0 0;font-size:14px;color:var(--grey)}
.cta .cta-promise::before{content:"\2713";color:var(--accent);font-weight:500}

.disclosure{
  margin-top:1.6rem;padding:1rem 1.25rem;background:var(--paper);
  border:1px solid var(--line-soft);border-radius:var(--r);
  font-size:13px;line-height:1.58;color:var(--muted);
}
.disclosure b{color:var(--ink-2);font-weight:500}

/* ---------- verified-by author block ---------- */
/* The disclosure block grew into this on 2026-10-18: the publisher panel and
   the named reviewer in one two-column cell, the same shape the Reverse
   Contact directory carries. Hairlines carry it, like everything else here. */
.author{background:var(--card);border:1px solid var(--line);padding:clamp(1.3rem,2.5vw,1.9rem) clamp(1.2rem,2.5vw,1.9rem)}
.author-body{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:clamp(1.4rem,3vw,2.6rem)}
.author-main .eyebrow{display:block;margin-bottom:.8rem}
.author-name{font-family:var(--display);font-weight:400;font-size:24px;letter-spacing:-.02em;margin:0 0 .9rem;line-height:1.2}
.author-name a{color:var(--ink);text-decoration:none}
.author-name a:hover{color:var(--accent-ink)}
.author-main p{margin:0 0 .75rem;font-size:15px;line-height:1.6;color:var(--ink-2)}
.author-main p:last-child{margin-bottom:0}
.author-note{font-size:14px;color:var(--muted)}
.author-note a{color:var(--accent-ink);text-decoration:underline;text-underline-offset:2px}
.author-side{border-left:1px solid var(--line-cell);padding-left:clamp(1.4rem,3vw,2.6rem)}
.reviewer{margin-bottom:1.2rem}
.reviewer .eyebrow{display:block;margin-bottom:.55rem}
.reviewer-name{margin:0 0 .4rem;font-size:16px;font-weight:500;color:var(--ink)}
.reviewer-name a{color:var(--ink);text-decoration:none}
.reviewer-name a:hover{color:var(--accent-ink)}
.reviewer-role{margin:0;font-size:14px;line-height:1.55;color:var(--muted)}
.author-meta{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.2rem}
.author-meta > div{border:1px solid var(--line-cell);background:var(--paper);padding:.45rem .8rem}
.author-meta dt{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--faint);margin-bottom:.2rem}
.author-meta dd{margin:0;font-size:14px;font-weight:500;color:var(--ink)}
.author-src .lbl{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);font-weight:400;display:block;margin-bottom:.5rem}
.author-src ul{list-style:none;margin:0;padding:0;display:grid;gap:.4rem}
.author-src li{font-size:14px;line-height:1.45;color:var(--ink-2);padding-left:1rem;position:relative}
.author-src li::before{content:"";position:absolute;left:0;top:.5em;width:4px;height:4px;background:var(--accent-raw)}
.author-src a{color:var(--ink-2);text-decoration:underline;text-decoration-color:var(--line-cell);text-underline-offset:2px}
.author-src a:hover{color:var(--ink)}
@media (max-width:900px){
  .author-body{grid-template-columns:1fr}
  .author-side{border-left:0;padding-left:0;border-top:1px solid var(--line-cell);padding-top:1.2rem}
}

.empty{padding:2.5rem 1.5rem;text-align:center;border:1px dashed var(--line);border-radius:var(--r-lg);color:var(--muted);font-size:15px}

/* ---------- inline tool chip ----------
   Plain inline, not inline-flex: a flex box creates its own baseline and
   drops the text that follows it out of line with the heading. */
/* A vendor logo that opens Frontal's referral link (affLogo in data.js). The link
   takes no box of its own: line-height 0 keeps a flex item exactly the logo's size,
   and as an inline box it leaves the logo's own vertical-align alone. */
a.aff{line-height:0;flex:none;text-decoration:none;color:inherit}
a.aff:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.tool{white-space:nowrap}
.tool > .tm,.tool > a.aff{margin-right:.45rem}
.tool .tm{vertical-align:-4px}

/* ---------- index list: rows, not another grid of cards ---------- */
.index-list{display:grid;border-top:1px solid var(--line)}
.index-row{
  display:grid;grid-template-columns:40px minmax(0,12.5rem) minmax(0,1fr) auto;
  gap:clamp(.9rem,2vw,1.6rem);align-items:baseline;
  padding:1.15rem .5rem;border-bottom:1px solid var(--line);
  text-decoration:none;transition:background .16s,padding-left .16s;
}
.index-row:hover{background:var(--card);padding-left:.9rem}
.index-row .n{font-family:var(--mono);font-size:var(--t-11);font-weight:400;color:var(--accent)}
.index-row h3{font-family:var(--sans);font-size:16px;font-weight:500;letter-spacing:-.02em;margin:0;line-height:1.3}
.index-row p{font-size:14px;color:var(--muted);line-height:1.52;margin:0}
.index-row .cnt{
  font-family:var(--mono);font-size:var(--t-11);color:var(--accent-ink);
  background:var(--accent-soft);border-radius:0;padding:.16rem .55rem;white-space:nowrap;
}
.index-row:hover h3{color:var(--accent-ink)}

/* Migration steps reuse the row without the hub's link columns: two children
   (a number and a prose div) against a grid whose second column reserves
   12.5rem for a short title link, which clipped the prose to 200px wide. The
   two-class selector also out-ranks the 640px .index-row rule below. */
.index-row.index-row--flow{grid-template-columns:40px minmax(0,1fr)}
.index-row.index-row--flow:hover{background:none;padding-left:.5rem}
.index-row.index-row--flow:hover h3{color:var(--ink)}
.index-row .index-num{font-family:var(--mono);font-size:var(--t-11);font-weight:400;color:var(--accent)}
.index-row--flow h3{margin:0 0 .45rem}
.index-row--flow p{margin:0 .8rem .5rem 0}
.index-row--flow p:last-child{margin-bottom:0}

/* ---------- rules: hairline definition list, no boxes ---------- */
.rules{border-top:1px solid var(--line);margin:0}
.rules > div{padding:1.1rem 0;border-bottom:1px solid var(--line)}
.rules dt{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);font-weight:400;margin-bottom:.45rem}
/* MCP tool names are case sensitive, so this list prints them as they are
   called rather than as an uppercase label. */
.rules--code dt{font-size:12px;letter-spacing:0;text-transform:none}
.rules dd{margin:0;font-size:15px;line-height:1.62;color:var(--ink-2)}

/* ---------- variants + ledger: for side-by-side costings ---------- */
.variants{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(304px,100%),1fr));gap:clamp(26px,4vw,60px)}
.variant > h3{font-family:var(--display);font-weight:400;font-size:20px;letter-spacing:-.02em;margin:0 0 .4rem;line-height:1.25}
.variant > .vnote{font-size:14px;color:var(--muted);line-height:1.55;margin:0}
.ledger{border-top:1px solid var(--line);margin:1.1rem 0 .9rem}
.ledger-row{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:.85rem;
  align-items:baseline;padding:.85rem 0;border-bottom:1px solid var(--line-soft);
}
.ledger-row .ln{
  width:22px;height:22px;border-radius:2px;background:var(--console);color:var(--cream);
  display:grid;place-items:center;font-family:var(--mono);font-size:var(--t-11);font-weight:400;
  align-self:start;margin-top:.12rem;
}
.ledger-row.is-alt .ln{background:var(--accent);color:#04060C}
.ledger-row .lname{display:block;font-size:15px;font-weight:500;color:var(--ink);line-height:1.35}
.ledger-row .lsub{display:block;font-size:13px;font-weight:400;color:var(--muted);line-height:1.5;margin-top:.2rem}
.ledger-row .lval{font-family:var(--mono);font-size:13px;font-weight:400;color:var(--ink);text-align:right;white-space:nowrap}
.ledger-row .lval small{display:block;font-family:var(--sans);font-size:12px;font-weight:400;color:var(--faint);margin-top:.15rem;white-space:normal}
.variant .vtotal{
  font-size:15px;line-height:1.6;color:var(--ink-2);
  border-left:3px solid var(--accent);padding-left:.85rem;
}
.variant .vtotal b{color:var(--ink)}

/* ---------- alternatives list on a product page ---------- */
.alt-list{display:grid;gap:.5rem;margin:1.2rem 0 1.1rem}
.alt-row{
  display:flex;align-items:center;gap:.85rem;
  background:var(--card);border:1px solid var(--line);border-radius:var(--r);
  padding:.8rem 1rem;text-decoration:none;transition:border-color .16s,transform .16s;
}
.alt-row:hover{background:rgba(255,255,255,.028)}
.alt-row .who{min-width:0;flex:1}
.alt-row .who b{display:block;font-size:15px;color:var(--ink);font-weight:500;line-height:1.3}
.alt-row .who span{display:block;font-size:13px;color:var(--muted);line-height:1.42;margin-top:.1rem}
.alt-row .rate{font-family:var(--mono);font-size:13px;color:var(--ink);white-space:nowrap;text-align:right}
.alt-row .rate small{display:block;font-size:var(--t-11);color:var(--faint);font-family:var(--sans)}

/* ---------- collapsible filter groups ---------- */
details.fgroup > summary.flabel{
  list-style:none;cursor:pointer;position:relative;padding-right:1.2rem;
  user-select:none;margin-bottom:0;
}
details.fgroup > summary.flabel::-webkit-details-marker{display:none}
details.fgroup > summary.flabel::after{
  content:"";position:absolute;right:.1rem;top:.32em;
  width:6px;height:6px;border-right:1.5px solid var(--muted);border-bottom:1.5px solid var(--muted);
  transform:rotate(45deg);transition:transform .18s;
}
details.fgroup[open] > summary.flabel::after{transform:rotate(-135deg);top:.5em}
details.fgroup[open] > summary.flabel{margin-bottom:.55rem}
details.fgroup > summary.flabel:hover{color:var(--accent-ink)}
summary.flabel .on{
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;
  background:var(--accent);color:#fff;border-radius:0;
  padding:.04rem .38rem;margin-left:.35rem;letter-spacing:0;
}

/* ---------- product page: content left, facts right ---------- */
.prod{display:grid;grid-template-columns:minmax(0,1fr) 330px;column-gap:clamp(24px,3.4vw,54px);row-gap:0;align-items:start}
.prod-main{min-width:0}
/* The source order is the title, the sidebar, then the article, which is the order a
   phone shows them in. The sidebar used to follow the article and CSS order lifted the
   factsheet above it, so a phone that painted while the HTML was still arriving drew
   the article first and pushed it down when the factsheet landed: CLS 0.20 on every
   review. Here the grid puts the sidebar back on the right, beside both rows. */
.prod > *{min-width:0}
.prod > .prod-head{grid-column:1;grid-row:1}
.prod > .prod-side{grid-column:2;grid-row:1 / span 2}
.prod > .prod-main{grid-column:1;grid-row:2}
/* The title's bottom margin used to collapse into the article's first top margin
   (a console's 1.5rem won, a paragraph took the title's 1.15rem). Grid items do not
   collapse, so on the two-column layout an empty first box carries the title's margin
   inside the article, where it collapses exactly as before. */
@media (min-width:1121px){
  .prod > .prod-head{margin-bottom:0}
  .prod > .prod-main::before{content:"";display:block;margin-bottom:1.15rem}
}
/* The whole sidebar is one sticky block: TOC, headline facts and CTA all move
   with the page together. Two earlier versions failed and should not return:
   - one sticky box with an inner scroll hid the CTA on every laptop (0-12% visible);
   - pinning only the TOC and CTA left the facts behind (Eugene: "static").
   It stays short enough to pin because the full factsheet opens in a dialog,
   and fitSidebar() in app.js trims parts marked data-trim on shorter screens. */
.prod-side{position:sticky;top:92px;display:grid;gap:.85rem;align-content:start;align-self:start}
.is-trimmed{display:none !important}

/* compact layouts used when the sidebar has to shed height */
.facts.is-compact{padding:.8rem .9rem .85rem}
.facts.is-compact .facts-visit.gf-button.gf-button[data-gf-theme]{margin-bottom:.35rem}
.facts.is-compact dl{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);column-gap:.9rem}
.facts.is-compact .fact{padding:.5rem 0}
.facts.is-compact .fact dd{font-size:13px;line-height:1.35}
/* billing chips read as a short line of text, instead of stacking three high */
.facts.is-compact .fact .pills{display:block;margin:0}
.facts.is-compact .fact .pills .pill{display:inline;background:none;border:0;padding:0;font-family:var(--sans);font-size:13px;font-weight:400;letter-spacing:0;text-transform:none;color:var(--ink)}
/* the space sits outside nowrap, so the line can break between items but not inside one */
.facts.is-compact .fact .pills .pill:not(:last-child)::after{content:", ";white-space:normal}
.facts.is-compact .facts-more{margin-top:.35rem;padding:.4rem}
.toc.is-compact{padding:.7rem .8rem .75rem}
.toc.is-compact:not(.expanded) li:nth-child(3){display:none}
.toc.is-compact h2{margin-bottom:.3rem}
.toc.is-compact a{padding:.32rem .5rem}
/* one row: label and toggle on top, then only the section being read */
.toc.is-mini:not(.expanded){display:grid;grid-template-columns:1fr auto;align-items:center;row-gap:.25rem}
.toc.is-mini:not(.expanded) h2{margin:0}
.toc.is-mini:not(.expanded) ol{grid-column:1 / -1}
.toc.is-mini:not(.expanded) li{display:none}
.toc.is-mini:not(.expanded) li:has(> a.active){display:block}
.toc.is-mini:not(.expanded) .toc-more{grid-row:1;grid-column:2;width:auto;margin:0;padding:.2rem .45rem;gap:.35rem;font-size:12px}
/* the open list is a panel over the factsheet; app.js keeps the slot collapsed */
/* The expanded panel floats over the factsheet, so it needs an opaque ground
   and a shadow dark enough to read against the page rather than the light one
   this inherited from the old brand. */
.toc.expanded{
  position:relative;z-index:5;background:var(--surface);border-color:#ffffff26;
  box-shadow:0 26px 54px -26px rgba(0,0,0,.75);
}
.toc.expanded ol{max-height:22rem;overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}
.prov{display:flex;align-items:center;gap:.5rem;padding:.3rem 0;text-decoration:none;color:var(--ink)}
.prov-name{font-size:14px}
.prov-price{margin-left:auto}
.fact.is-compact dd{display:flex;flex-wrap:wrap;gap:.35rem}
.fact.is-compact .prov{padding:0}
/* the compact factsheet shows the marks alone, so they take the site's full 28px tile */
.fact.is-compact .prov .tm{--tm-k:1!important}
.fact.is-compact .prov-name,.fact.is-compact .prov-price{display:none}
.other-stack{display:block;padding:.22rem 0;font-size:14px;color:var(--accent-ink);text-decoration:none}

/* ---------- full factsheet dialog ---------- */
html.has-dialog{overflow:hidden}
.facts-dialog{
  width:min(760px, calc(100vw - 32px));max-height:min(86vh, 900px);
  padding:0;border:1px solid #ffffff26;border-radius:var(--r-xl);
  background:var(--surface);color:var(--ink-2);
  box-shadow:0 44px 100px -34px rgba(0,0,0,.6);overflow:hidden;
}
.facts-dialog[open]{display:flex;flex-direction:column;animation:fd-in .18s ease-out}
.facts-dialog::backdrop{background:rgba(4,6,12,.82);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px)}
@keyframes fd-in{from{opacity:0;transform:translateY(8px) scale(.985)}to{opacity:1;transform:none}}
.fd-head{display:flex;align-items:center;gap:.85rem;padding:1.1rem 1.25rem;border-bottom:1px solid var(--line-soft);flex:none}
.fd-titles{min-width:0}
.fd-head h2{font-family:var(--display);font-weight:400;font-size:24px;letter-spacing:-.03em;line-height:1.1;margin:0}
.fd-sub{display:block;font-size:13px;color:var(--muted);margin-top:.2rem}
.fd-close{
  margin-left:auto;flex:none;width:38px;height:38px;border-radius:2px;cursor:pointer;
  border:1px solid var(--line);background:var(--paper);color:var(--ink);
  display:grid;place-items:center;transition:border-color .15s,background .15s;
}
.fd-close:hover{border-color:var(--ink);background:var(--card)}
.fd-close:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.fd-body{overflow-y:auto;padding:.3rem 1.25rem 1.1rem;overscroll-behavior:contain}
.fd-grid{display:grid;grid-template-columns:1fr 1fr;column-gap:1.8rem;margin:0}
.fd-grid > .fact:nth-child(-n+2){border-top:0}
.fd-sub .mono{white-space:nowrap}
.fd-foot{display:flex;flex-wrap:wrap;align-items:center;gap:12px 16px;padding:12px 20px;border-top:1px solid var(--line-soft);background:var(--paper);flex:none}
.fd-note{margin-left:auto;text-wrap:pretty}
@media (max-width:600px){
  .fd-grid{grid-template-columns:1fr}
  .fd-grid > .fact:nth-child(2){border-top:1px solid var(--line-soft)}
  .fd-note{margin-left:0;width:100%}
}
@media (prefers-reduced-motion:reduce){ .facts-dialog[open]{animation:none} }

/* narrow screens have no sidebar: a full CTA card sits inside the article instead */
.prod-main > .cta-card--inline{display:none;margin-top:2.4rem}

.prod-head{display:flex;gap:1.05rem;align-items:flex-start;margin-bottom:1.15rem}
.prod-head h1{
  font-family:var(--display);font-weight:400;letter-spacing:-.03em;line-height:1.04;
  font-size:var(--t-h1);margin:0;display:inline;
}
.prod-head h1 em{font-style:normal;color:var(--accent)}
.prod-titles{min-width:0;padding-top:.15rem}
/* The featured mark is an icon with a tooltip rather than a word (Eugene,
   2026-10-13). It used to set as a pill reading FEATURED next to the h1, which
   is a lot of chrome for a one-word editorial note, and a pill is a radius the
   rest of this design does not use. What "featured" means is on the homepage
   method rules, so the tooltip says it rather than making the reader go and
   look: an editorial pick. */
.badge-feat{
  position:relative;display:inline-flex;align-items:center;justify-content:center;
  vertical-align:middle;margin-left:.55rem;width:22px;height:22px;
  background:var(--paper);border:1px solid var(--line);color:var(--accent);cursor:help;
}
.badge-feat svg{display:block}
.badge-feat:hover{border-color:var(--sand);background:rgba(165,204,253,.1)}
.badge-feat:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
/* The tooltip floats over the page, so it takes --surface, never --card or
   --paper: that is the rule the facts dialog and the TOC panel are held to.
   It is anchored to the badge's LEFT edge rather than centred, and clamped
   against the viewport, because a 240px box centred on a 22px icon near the
   left gutter hangs off the page and makes it scroll sideways at 400px. */
.badge-feat::after{
  content:attr(data-tip);
  /* Opens downward. The badge sits in the page header, a few rows under a
     sticky 64px nav, so a tooltip above it is the one place on the page
     guaranteed to be covered. */
  position:absolute;left:-1px;top:calc(100% + 9px);width:max-content;
  max-width:min(17rem,62vw);
  background:var(--surface);border:1px solid #ffffff26;color:var(--ink-2);
  font-family:var(--sans);font-size:12px;font-weight:400;line-height:1.45;
  letter-spacing:0;text-transform:none;text-align:left;padding:.55rem .7rem;
  box-shadow:0 18px 38px -20px rgba(0,0,0,.8);
  pointer-events:none;z-index:40;
  /* display:none, not visibility:hidden. A hidden box still contributes to the
     document's scroll width, and this one is 247px wide: at 400px it took the
     Apollo review to a scrollWidth of 589 and made the whole page slide
     sideways. verify-agent.py caught it at 400px; verify-layout.py checks the
     same thing on every page. */
  display:none;
}
.badge-feat:hover::after,.badge-feat:focus-visible::after{display:block}
.prod-sub{font-size:16px;color:var(--ink-2);margin:.55rem 0 0}
.byline{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;color:var(--muted);margin-top:.6rem}
.byline b{color:var(--ink);font-family:var(--sans);font-size:13px;letter-spacing:-.005em;font-weight:500}
.byline i{font-style:normal;color:var(--faint)}

.prod-main > section{padding-top:2.2rem;scroll-margin-top:92px}
.prod-main > section:first-of-type{padding-top:1.4rem}
.prod-main h2.psec{
  font-family:var(--display);font-weight:400;letter-spacing:-.03em;line-height:1.08;
  font-size:var(--t-h2-sm);margin:0 0 .7rem;
}
.prod-main h2.psec em{font-style:normal;color:var(--accent)}

/* on-this-page, as in their article template: mono title, and the active row
   marked by a blue left rule rather than a fill */
.toc{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:.95rem 1rem 1rem}
.toc h2{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--faint);font-weight:400;margin:0 0 .6rem;padding-left:.55rem}
.toc ol{list-style:none;margin:0;padding:0;display:grid;gap:.05rem}
.toc a{
  display:flex;gap:.62rem;align-items:baseline;
  padding:.42rem .55rem;border-radius:2px;
  font-size:14px;line-height:1.35;color:var(--ink-2);text-decoration:none;
  transition:background .15s,color .15s;
}
.toc a:hover{background:var(--paper);color:var(--ink)}
.toc a.active{background:transparent;color:var(--accent);font-weight:400;box-shadow:inset 2px 0 0 var(--accent)}
.toc a .num{font-family:var(--mono);font-size:var(--t-11);color:var(--faint);flex:none}
.toc a.active .num{color:var(--accent)}
.toc li.extra{display:none}
.toc.expanded li.extra{display:block}
.toc-more{
  width:100%;margin-top:.35rem;padding:.42rem;border:0;background:transparent;
  font-family:var(--sans);font-size:13px;font-weight:500;color:var(--muted);
  cursor:pointer;display:flex;align-items:center;justify-content:space-between;
  border-radius:2px;
}
.toc-more:hover{background:var(--paper);color:var(--ink)}

/* factsheet */
.facts{background:var(--card);border:1px solid var(--line);border-radius:var(--r-lg);padding:1.05rem 1.1rem 1.15rem}
/* the Visit button spans the factsheet with its fill, not its 44px target, on the content edges:
   the hairlines under it and the See all chip start and end where its fill does */
.facts .gd-btn.gf-button.gf-button[data-gf-theme]{width:calc(100% + 8px);max-width:none;margin:0 -4px .5rem}
.facts dl{margin:0}
.fact{padding:.72rem 0;border-top:1px solid var(--line-soft)}
.fact dt{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--faint);font-weight:400;margin-bottom:.32rem}
.fact dd{margin:0;font-size:14px;color:var(--ink);line-height:1.45}
.fact dd a{color:var(--accent-ink)}
.fact .pills{display:flex;flex-wrap:wrap;gap:.3rem;margin-top:.1rem}
.fact .pills .pill{font-size:var(--t-11);padding:.2rem .5rem}
.facts-more{
  width:100%;margin-top:.7rem;padding:.52rem;cursor:pointer;
  border:1px solid var(--line);border-radius:var(--r-sm);background:transparent;
  font-family:var(--sans);font-size:13px;font-weight:500;color:var(--muted);
  display:flex;align-items:center;justify-content:center;gap:.4rem;transition:.15s;
}
.facts-more:hover{border-color:rgba(165,204,253,.45);color:var(--accent-ink);background:var(--accent-soft)}
.facts-more svg{transition:transform .2s}
.facts-more:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* ---------- hub: comparison pairs ---------- */
.pairs{align-items:stretch;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(316px,100%),1fr));gap:0;border-top:1px solid var(--line-cell);border-left:1px solid var(--line-cell)}
.pair{
  background:transparent;border:0;border-right:1px solid var(--line-cell);border-bottom:1px solid var(--line-cell);border-radius:0;
  padding:1.1rem 1.2rem 1.15rem;text-decoration:none;display:flex;flex-direction:column;gap:.6rem;
  transition:border-color .18s,transform .18s,box-shadow .18s;
}
.pair:hover{background:rgba(255,255,255,.028)}
.pair-top{display:flex;align-items:center;gap:.55rem}
.pair-top .vs{
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;text-transform:uppercase;letter-spacing:.04em;color:var(--accent);
  padding:0 .1rem;flex:none;
}
.pair h3{font-family:var(--sans);font-size:16px;font-weight:500;letter-spacing:-.01em;margin:0;line-height:1.3}
.pair p{font-size:14px;color:var(--muted);line-height:1.5;margin:0}
.pair .pair-meta{display:flex;flex-wrap:wrap;gap:.35rem;margin-top:auto;padding-top:.5rem}
.pair .go{font-size:13px;font-weight:500;color:var(--ink);display:inline-flex;gap:.3rem;align-items:center}
.pair:hover .go{color:var(--accent-ink)}

/* head-to-head hero */
.vs-head{display:grid;grid-template-columns:1fr auto 1fr;gap:clamp(12px,2.5vw,32px);align-items:center;margin-bottom:1.4rem}
.vs-side{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.5rem}
.vs-side h2{font-family:var(--sans);font-size:18px;font-weight:500;margin:0;letter-spacing:-.012em}
.vs-side .tag{font-size:13px;color:var(--muted);line-height:1.4}
.vs-mid{
  font-family:var(--mono);font-size:12px;font-weight:400;text-transform:uppercase;letter-spacing:.04em;color:var(--cream);
  width:52px;height:52px;border-radius:2px;display:grid;place-items:center;
  border:1px solid rgba(165,204,253,.32);background:linear-gradient(rgba(165,204,253,.16), rgba(165,204,253,.16)), var(--console);
  box-shadow:var(--glow);
}
.vs-verdict{
  background:var(--card);border:1px solid var(--line);border-left:3px solid var(--accent);
  border-radius:var(--r);padding:1.15rem 1.3rem;
}
.vs-verdict .lbl{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--accent-ink);font-weight:400;display:block;margin-bottom:.45rem}
.vs-verdict p{font-size:16px;line-height:1.58;color:var(--ink);margin:0}

/* ---------- hub: stacks ---------- */
.stack-cards{align-items:stretch;display:grid;grid-template-columns:repeat(auto-fill,minmax(min(340px,100%),1fr));gap:0;border-top:1px solid var(--line-cell);border-left:1px solid var(--line-cell)}
.stack-card{
  background:transparent;border:0;border-right:1px solid var(--line-cell);border-bottom:1px solid var(--line-cell);border-radius:0;
  padding:1.2rem 1.3rem 1.25rem;text-decoration:none;display:flex;flex-direction:column;gap:.6rem;
  transition:border-color .18s,transform .18s,box-shadow .18s;
}
.stack-card:hover{background:rgba(255,255,255,.028)}
.stack-card.is-featured{background:rgba(165,204,253,.06)}
.stack-card h3{font-family:var(--sans);font-size:16px;font-weight:500;letter-spacing:-.012em;margin:0;line-height:1.28}
.stack-card p{font-size:14px;color:var(--muted);line-height:1.52;margin:0}
.chain{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem}
.chain .arw{color:var(--faint);font-size:12px;flex:none}
.stack-nums{display:flex;gap:1.3rem;padding-top:.6rem;border-top:1px solid var(--line-soft);margin-top:auto}
.stack-nums div{display:flex;flex-direction:column}
.stack-nums b{font-family:var(--mono);font-size:13px;color:var(--ink);font-weight:400;line-height:1.2}
.stack-nums span{font-size:12px;color:var(--muted);margin-top:.15rem}

/* ---------- CTA card: always the 5th item in a card grid ----------
   Spans the full row so it never leaves an orphan cell beside it,
   whatever the column count. Container queries size it to the grid
   it lands in, not the viewport. Styled as a Frontal homepage panel:
   near-black, fading 32px grid, orange bloom, mono labels. */
.cta-card{
  grid-column:1 / -1;
  position:relative;isolation:isolate;overflow:hidden;
  container-type:inline-size;
  background:var(--console);color:var(--grey);
  border:1px solid rgba(255,255,255,.08);
  border-radius:var(--r-xl);
  box-shadow:var(--shadow-lg);
  transition:transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.cta-card::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size:32px 32px;
  -webkit-mask-image:radial-gradient(ellipse 62% 96% at 80% 50%, #000, transparent 74%);
  mask-image:radial-gradient(ellipse 62% 96% at 80% 50%, #000, transparent 74%);
}
.cta-card::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(460px circle at 78% 46%, rgba(165,204,253,.2), transparent 70%);
}
.cta-card:hover{border-color:rgba(165,204,253,.28);box-shadow:0 2px 4px rgba(0,0,0,.08), 0 34px 70px -34px rgba(165,204,253,.35)}

.cc-inner{
  display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);
  gap:clamp(22px,4cqi,52px);align-items:center;
  padding:clamp(24px,4.2cqi,46px);
}
.cc-copy{min-width:0}

/* the bracketed mono label, on dark */
.cc-eyebrow{
  display:inline-flex;align-items:center;gap:.55em;
  font-family:var(--mono);font-size:var(--t-11);font-weight:400;letter-spacing:.04em;text-transform:uppercase;
  line-height:1.4;color:var(--accent);margin-bottom:1.05rem;
}

.cta-card h3{
  font-family:var(--display);font-weight:400;color:#FFFFFF;
  font-size:28px;
  line-height:1.06;letter-spacing:-.03em;margin:0 0 .85rem;
}
.cta-card h3 em{font-style:normal;color:var(--accent)}
.cc-desc{font-size:16px;line-height:1.62;color:var(--grey);margin:0 0 1.25rem;max-width:56ch}

.cc-stats{
  display:flex;flex-wrap:wrap;gap:.7rem 1.8rem;
  margin:0 0 1.4rem;padding:.95rem 0;
  border-block:1px solid rgba(255,255,255,.09);
}
.cc-stats b{display:block;font-family:var(--display);font-size:28px;font-weight:400;color:var(--cream);line-height:1;letter-spacing:-.04em}
.cc-stats span{display:block;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--dim);margin-top:.5rem}

.cc-actions{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem 1.1rem}
/* the booking bar (email first) sits where the button was; its error line hangs
   below it, so the row keeps room for it */
.cc-actions .gf-booking{flex:1 1 320px;max-width:402px}
.cc-actions{row-gap:1.6rem}
/* Frontal's written promise, with their check mark */
.cc-note{font-size:13px;line-height:1.45;color:var(--grey);max-width:30ch}
.cc-note::before{content:"\2713\00a0";color:var(--accent);font-weight:500}

/* The visual: Frontal's partner tools as a still row of logos, then two outcomes
   labelled by account type and signal. No "live" badge, no looping connectors and
   no named company or person: the site's rule for every product visual. */
.cta-visual{min-width:0;container-type:inline-size}
.cv-frame{
  background:rgba(255,255,255,.035);border:1px solid rgba(255,255,255,.1);
  border-radius:0;padding:1.1rem 1.1rem 1.15rem;
}
.cv-label{display:block;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:.85rem}
.cv-logos{display:flex;flex-wrap:wrap;gap:12px 14px;padding-bottom:1.05rem;margin-bottom:1.05rem;border-bottom:1px solid rgba(255,255,255,.1)}
.cv-logo{display:flex;flex-direction:column;align-items:center;gap:.45rem;min-width:48px}
.cv-logo b{font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.02em;color:var(--muted)}
.cv-logos{flex-wrap:nowrap;justify-content:space-between}
@container (max-width:439px){.cv-logo{min-width:0}.cv-logo b{display:none}}

/* routed results, as the orange-edged account card in Frontal's hero */
.cv-meeting{
  display:flex;align-items:center;gap:.75rem;
  background:rgba(255,255,255,.04);border:1px solid rgba(165,204,253,.55);
  border-radius:var(--r);padding:.6rem .75rem;
  box-shadow:0 16px 36px -20px rgba(165,204,253,.45);
}
.cv-meeting + .cv-meeting{margin-top:.5rem;border-color:rgba(255,255,255,.1);box-shadow:none}
.cv-cal,.cv-score{flex:none;width:48px;border-radius:2px;overflow:hidden;text-align:center;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}
.cv-cal i,.cv-score i{display:block;font-style:normal;font-family:var(--mono);background:rgba(165,204,253,.2);color:#A5CCFD;font-size:var(--t-11);font-weight:400;letter-spacing:.04em;text-transform:uppercase;padding:.16rem 0}
.cv-cal b,.cv-score b{display:block;font-family:var(--mono);font-size:var(--t-11);font-weight:400;color:var(--cream);padding:.24rem 0}
.cv-txt{min-width:0;flex:1}
.cv-txt b{display:block;font-size:13px;font-weight:500;color:var(--cream);line-height:1.3}
.cv-txt small{display:block;font-size:12px;color:var(--dim)}
/* the account and the person are grey placeholder bars, as in the site's service screens
   (.sx-ph): a graphic never names a company, a person or a type label such as "Series B" */
.cv-sub{display:flex;align-items:center;gap:.5rem;min-height:16px;margin-top:.12rem}
.cv-ph{display:block;flex:none;width:var(--w,80px);max-width:100%;height:8px;border-radius:4px;background:rgba(255,255,255,.14)}
.cv-cap{display:block;margin-top:.7rem;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);text-align:right}
.cv-ok{flex:none;width:22px;height:22px;border-radius:2px;background:rgba(165,204,253,.16);color:var(--emerald);display:grid;place-items:center;font-size:12px;font-weight:500}

@container (max-width:740px){
  .cc-inner{grid-template-columns:1fr}
  .cta-card h3{font-size:32px}
}
@container (max-width:420px){
  .cv-logos{gap:10px}
  .cv-logo .tm{--tm-k:1.2143!important}
  .cc-stats{gap:.6rem 1.2rem}
}
@media (prefers-reduced-motion:reduce){
  .cta-card,.cta-card:hover{transition:none;transform:none}
}

/* ---------- structure scheme: how the page types connect ----------
   One SVG on desktop, the same relationships as a list on phones, because a
   wide diagram at 400px is unreadable however hard you scale it. */
.scheme{margin-top:1.8rem}
.scheme svg{width:100%;height:auto;display:block}
.scheme .box{fill:var(--card);stroke:var(--line);stroke-width:1}
.scheme .box-dark{fill:var(--console);stroke:rgba(165,204,253,.32)}
.scheme .box-spine{stroke:rgba(165,204,253,.45)}
.scheme .t-label{font-family:var(--mono);font-size:var(--t-11);letter-spacing:1.1px;text-transform:uppercase;fill:var(--faint)}
.scheme .t-title{font-family:var(--sans);font-size:15px;font-weight:500;letter-spacing:-.3px;fill:var(--ink)}
.scheme .t-kw{font-family:var(--mono);font-size:var(--t-11);fill:var(--accent-ink)}
.scheme .box-dark + .t-title,.scheme .on-dark.t-title{fill:var(--cream)}
.scheme .on-dark.t-label{fill:var(--dim)}
.scheme .on-dark.t-kw{fill:#A5CCFD}
.scheme .link{stroke:var(--sand);stroke-width:1.5;fill:none}
.scheme .link-strong{stroke:var(--accent);stroke-width:1.5}
.scheme .t-link{font-family:var(--mono);font-size:var(--t-11);letter-spacing:.6px;text-transform:uppercase;fill:var(--muted)}
.scheme-list{display:none;border-top:1px solid var(--line);margin-top:1.4rem}
.scheme-list > div{padding:.95rem 0;border-bottom:1px solid var(--line)}
.scheme-list dt{font-family:var(--sans);font-size:15px;font-weight:500;color:var(--ink);margin-bottom:.3rem}
.scheme-list .kw{font-family:var(--mono);font-size:12px;color:var(--accent-ink);display:block;margin-bottom:.35rem}
.scheme-list dd{margin:0;font-size:14px;line-height:1.55;color:var(--ink-2)}
@media (max-width:820px){
  .scheme svg{display:none}
  .scheme-list{display:block}
}

/* ---------- page CTA placed inside an article column, above its FAQ ---------- */
.cta--inline{margin-top:2.6rem}
.cta--inline h2{font-size:var(--t-h2-sm)}

/* ---------- compact CTA: the sidebar version of .cta-card ---------- */
.cta-mini{
  position:relative;isolation:isolate;overflow:hidden;
  background:var(--console);color:var(--grey);
  border:1px solid rgba(255,255,255,.08);border-radius:var(--r-lg);
  padding:1.15rem 1.15rem 1.2rem;
  transition:border-color .2s ease;
}
.cta-mini::before{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:linear-gradient(rgba(255,255,255,.045) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.045) 1px, transparent 1px);
  background-size:24px 24px;
  -webkit-mask-image:radial-gradient(ellipse 80% 60% at 90% 0%, #000, transparent 75%);
  mask-image:radial-gradient(ellipse 80% 60% at 90% 0%, #000, transparent 75%);
}
.cta-mini::after{
  content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(260px circle at 92% 6%, rgba(165,204,253,.24), transparent 70%);
}
.cta-mini:hover{border-color:rgba(165,204,253,.4)}
.cta-mini .cc-eyebrow{font-size:var(--t-11);margin-bottom:.8rem}
.cm-logos{display:flex;gap:6px;margin:0 0 .9rem}
.cta-mini h3{
  font-family:var(--display);font-weight:400;color:#FFFFFF;
  font-size:20px;line-height:1.1;letter-spacing:-.03em;margin:0 0 .55rem;
}
.cta-mini h3 em{font-style:normal;color:var(--accent)}
.cm-desc{font-size:14px;line-height:1.55;color:var(--grey);margin:0 0 .9rem}
.cm-stats{
  display:grid;grid-template-columns:1fr 1fr;gap:.5rem;
  margin:0 0 .95rem;padding:.7rem 0;border-block:1px solid rgba(255,255,255,.09);
}
.cm-stats b{display:block;font-family:var(--display);font-size:20px;font-weight:400;color:var(--cream);line-height:1;letter-spacing:-.03em}
.cm-stats span{display:block;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--dim);margin-top:.42rem;line-height:1.5}
.cta-mini .gf-booking{width:100%}
.cta-mini .gf-booking-form{padding-left:12px;gap:8px}
.cta-mini .gf-booking .gf-button.gf-button[data-gf-theme]{min-width:132px;--button-pad-x:12px}
.cta-mini .cc-note{display:block;max-width:none;margin-top:.6rem;font-size:12px;text-align:center;text-wrap:balance}
/* a factsheet that opens with facts (stacks) rather than a Visit button needs no rule on top */
.facts > dl:first-child > .fact:first-child{border-top:0;padding-top:.15rem}

/* ---------- responsive ---------- */
@media (max-width:1120px){
  .prod{grid-template-columns:1fr}
  .prod > *{grid-column:auto;grid-row:auto}
  /* dissolve the sidebar so the factsheet sits between the title and the article,
     in source order; the sidebar CTA gives way to the full card inside the article */
  .prod-side{display:contents}
  .prod-side > .cta-mini{display:none}
  .prod-main > .cta-card--inline{display:block}
  .toc{display:none}
}
@media (max-width:980px){
  .wrap-narrow{grid-template-columns:1fr;gap:1.6rem}
  .wrap-narrow > .col-head{position:static}
  .wrap-narrow > .col-head .lede{max-width:62ch;font-size:16px}
}
@media (max-width:640px){
  .index-row{grid-template-columns:34px minmax(0,1fr) auto;row-gap:.4rem;padding:1rem .4rem}
  .index-row p{grid-column:2/-1}
  .index-row .cnt{grid-column:3;grid-row:1}
}
@media (max-width:1000px){
  .hero-grid{grid-template-columns:1fr}
  .dir{grid-template-columns:1fr}
  .filters{position:static;max-height:none}
  .quotes{columns:2}
  .stats{grid-template-columns:repeat(2,1fr)}
  .stat:nth-child(2){border-right:0}
  .stat:nth-child(-n+2){border-bottom:1px solid var(--line-soft)}
}
@media (max-width:720px){
  body{font-size:16px}
  .quotes{columns:1}
  .score-row{grid-template-columns:1fr auto;gap:.35rem 1rem}
  .score-row .score-bar{grid-column:1;max-width:110px}
  .score-row .why{grid-column:1/-1}
  .guide-item{grid-template-columns:1fr;gap:.3rem}
  .step{grid-template-columns:auto minmax(0,1fr);row-gap:.55rem}
  .step .cost{grid-column:2;text-align:left}
  .stats{grid-template-columns:1fr}
  .stat{border-right:0;border-bottom:1px solid var(--line-soft)}
  .stat:last-child{border-bottom:0}
}

/* ---------- the before-you-build checklist ---------- */
/* Hairlines rather than cards, like .guide and .rules. The visual state is
   driven from :checked, never from a class: prerender.py renders app.js into
   the HTML, and a class written onto a row by JavaScript would be baked into
   the file and shipped ticked. */
.checklist{display:grid;gap:0;border-top:1px solid var(--line);margin-top:1.4rem}
.cl-row{
  display:grid;grid-template-columns:22px minmax(0,1fr);gap:clamp(12px,1.6vw,20px);
  padding:1.15rem 0;border-bottom:1px solid var(--line);cursor:pointer;
  align-items:start;transition:background .12s ease;
}
.cl-row:hover{background:var(--paper)}
.cl-row input{
  appearance:none;-webkit-appearance:none;margin:.2rem 0 0;width:16px;height:16px;
  border:1px solid var(--line-strong,var(--sand));background:transparent;border-radius:0;
  display:grid;place-content:center;cursor:pointer;flex:none;
}
.cl-row input:checked{background:var(--accent);border-color:var(--accent)}
.cl-row input:checked::after{
  content:"";width:9px;height:5px;border-left:1.5px solid #04060C;border-bottom:1.5px solid #04060C;
  transform:translateY(-1px) rotate(-45deg);
}
.cl-row input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.cl-body{display:block;min-width:0}
.cl-body b{
  display:block;color:var(--ink);font-family:var(--display);font-weight:400;
  font-size:18px;letter-spacing:-.02em;line-height:1.3;margin-bottom:.4rem;
}
.cl-p{display:block;font-size:15px;line-height:1.62;color:var(--ink-2)}
.cl-row input:checked ~ .cl-body b{color:var(--ink-2);text-decoration:line-through;text-decoration-thickness:1px}
.cl-row input:checked ~ .cl-body .cl-p{opacity:.62}
@media (max-width:520px){
  .cl-row{grid-template-columns:20px minmax(0,1fr);gap:12px}
}

/* ---------- what practitioners say ---------- */
/* Hairline rows like .guide, with the quote set in the serif so it reads as
   somebody else's voice rather than as ours. The citation is mono, because
   every label on this site is. */
.ugc{display:grid;gap:0;border-top:1px solid var(--line)}
.ugc-item{padding:1.6rem 0;border-bottom:1px solid var(--line)}
.ugc-item h3{
  font-family:var(--display);font-weight:400;font-size:20px;
  letter-spacing:-.02em;line-height:1.26;margin:0 0 .9rem;color:var(--ink);
}
.ugc-quote{
  margin:0 0 .7rem;padding:0 0 0 1.1rem;border-left:1px solid var(--accent);
  font-family:var(--display);font-weight:400;font-size:18px;
  line-height:1.56;letter-spacing:-.01em;color:var(--ink-2);
}
.ugc-cite{
  margin:0 0 .8rem;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;
  text-transform:uppercase;color:var(--muted);
}
.ugc-cite a{color:var(--accent);text-decoration:none}
.ugc-cite a:hover{text-decoration:underline}
.ugc-read{margin:0;font-size:15px;line-height:1.64;color:var(--ink-2)}
/* `reading` takes an array, and two paragraphs with margin:0 run together
   into a wall. It showed on two blocks and now shows on nine. */
.ugc-read + .ugc-read{margin-top:.75rem}

/* ---------- the vendor's own video ---------- */
/* A facade, not an embed: the markup is a plain link to the watch page, and
   app.js replaces it with a youtube-nocookie iframe on click. So no third
   party is contacted until the reader asks for the video, and a page with no
   JavaScript still carries a working link to it.
   There is deliberately no bitmap thumbnail. A YouTube still is a bright
   marketing frame, and on a page built out of hairlines it would be the only
   photograph and the loudest thing on it. The panel is a cell like any other,
   with the play triangle corner-ticked the way every primary action here is. */
.vid{
  position:relative;display:block;margin-top:1.4rem;aspect-ratio:16/9;
  background:var(--card);border:1px solid var(--line);text-decoration:none;
  overflow:hidden;color:var(--ink);
}
.vid:hover{background:var(--paper);border-color:var(--sand)}
.vid:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.vid-play{
  position:absolute;left:50%;top:46%;transform:translate(-50%,-50%);
  width:62px;height:62px;display:grid;place-items:center;color:var(--accent);
}
/* the same four corner gradients as the site's buttons, so the affordance is the
   site's own device rather than a new one */
.vid-play::before,.vid-play::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:.6}
.vid-play::before{
  background:
    linear-gradient(currentColor,currentColor) 0 0/9px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 0/9px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 0 100%/9px 1px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 100%/9px 1px no-repeat;
}
.vid-play::after{
  background:
    linear-gradient(currentColor,currentColor) 0 0/1px 9px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 0/1px 9px no-repeat,
    linear-gradient(currentColor,currentColor) 0 100%/1px 9px no-repeat,
    linear-gradient(currentColor,currentColor) 100% 100%/1px 9px no-repeat;
}
.vid-play b{
  display:block;width:0;height:0;margin-left:3px;
  border-left:15px solid var(--accent);
  border-top:9px solid transparent;border-bottom:9px solid transparent;
}
.vid:hover .vid-play{color:var(--cream)}
.vid:hover .vid-play b{border-left-color:var(--cream)}
.vid-body{
  position:absolute;left:0;right:0;bottom:0;display:block;
  padding:1.1rem 1.25rem;border-top:1px solid var(--line-soft);
  background:linear-gradient(rgba(4,6,12,0),rgba(4,6,12,.55));
}
.vid-eyebrow{
  display:block;font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;
  text-transform:uppercase;color:var(--muted);margin-bottom:.45rem;
}
.vid-name{
  display:block;font-family:var(--display);font-weight:400;
  font-size:18px;letter-spacing:-.02em;line-height:1.3;color:var(--ink);
}
.vid-sub{display:block;margin-top:.4rem;font-size:13px;color:var(--muted)}
.vid-logo{position:absolute;top:1.15rem;left:1.25rem}
.vid-data{display:none}
.vid-read{margin-top:1.3rem;font-size:15px;line-height:1.66;color:var(--ink-2)}
/* the iframe app.js swaps in, in the facade's own box */
.vid-frame{
  margin-top:1.4rem;aspect-ratio:16/9;border:1px solid var(--line);background:#000;
}
.vid-frame iframe{display:block;width:100%;height:100%;border:0}
/* the pending note: the slot is visible and says what was searched, rather
   than a page quietly missing a section its neighbours have */
.vid--pending{
  aspect-ratio:auto;padding:1.3rem 1.4rem;cursor:default;
}
.vid--pending:hover{background:var(--card);border-color:var(--line)}
.vid--pending p{margin:0;font-size:15px;line-height:1.64;color:var(--ink-2)}
.vid--pending p.tiny{margin-top:.7rem}
@media (max-width:520px){
  .vid-play{width:50px;height:50px;top:42%}
  .vid-play b{border-left-width:12px;border-top-width:7px;border-bottom-width:7px}
  .vid-body{padding:.9rem 1rem}
  .vid-logo{top:.9rem;left:1rem}
}

/* ---------- the expansion batch, 2026-10-14 ----------
   The cost model, the decision tool and the differences sort. Everything here
   is hairlines and cells: no radius above the pill, no fill beyond the 5%
   panel wash, no shadow. See CLAUDE.md section 6. */

/* a tier's own heading is an h4 under the provider's h3, because the outline
   runs section h2 -> provider h3 -> tier h4. .tier h3 is the review pages,
   where the tier sits directly under the section. */
.tier h4{font-family:var(--sans);font-size:16px;font-weight:500;margin:0 0 .5rem;color:var(--ink)}

/* ---------- cost model ---------- */
.cost{margin-top:1.6rem;border-top:1px solid var(--line);border-left:1px solid var(--line)}
.cost-controls{
  display:flex;flex-wrap:wrap;gap:0;
  border-right:1px solid var(--line);border-bottom:1px solid var(--line);
  background:rgba(255,255,255,.05);
}
.cost-field{
  display:flex;align-items:center;gap:.6rem;padding:.85rem 1.15rem;
  border-right:1px solid var(--line-cell);
  font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);
}
.cost-field:last-child{border-right:0}
.cost-field em{font-style:normal;color:var(--muted)}
.cost-field input{
  /* 9ch, because 7.5 clipped its own default: "10000" rendered as "1000(" at
     1440px and a reader cannot check a number they cannot see. A volume here
     can reach seven figures. */
  width:9ch;background:var(--console);color:var(--ink);
  border:1px solid var(--line-cell);border-radius:0;
  font-family:var(--mono);font-size:13px;padding:.35rem .45rem;text-align:right;
}
.cost-field input:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.cost-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr))}
.cost-side{
  padding:1.25rem 1.3rem 1.4rem;
  border-right:1px solid var(--line-cell);border-bottom:1px solid var(--line-cell);
}
.cost-side h3{font-family:var(--sans);font-size:16px;font-weight:500;margin:0 0 .45rem;color:var(--ink)}
.cost-side p.tiny{margin:0 0 .8rem}
.cost-side .kv-rows{padding:0;gap:.3rem}
.kv-rows--warn{margin-top:.55rem}
.kv-rows--warn .kv-row{border-color:rgba(165,204,253,.3)}
.kv-rows--warn .kv-row b{color:var(--accent)}

/* ---------- decision tool ---------- */
.decide{margin-top:1.6rem;border-top:1px solid var(--line);border-left:1px solid var(--line)}
.decide-q{
  margin:0;border:0;
  border-right:1px solid var(--line-cell);border-bottom:1px solid var(--line-cell);
  padding:1.5rem 1.3rem 1.2rem;
}
.decide-q legend{
  font-family:var(--sans);font-size:15px;font-weight:500;color:var(--ink);
  padding:.95rem 0 0;margin:0 0 .7rem;
}
/* Chromium renders a legend at the fieldset's top edge and ignores its
   margin-top, which is why the question sat glued to the border no matter
   what padding the fieldset carried. Padding on the legend itself is the one
   thing that moves the glyphs down (measured 2026-10-18). */
.decide-opt{
  display:flex;align-items:flex-start;gap:.6rem;padding:.32rem 0;cursor:pointer;
  font-size:15px;line-height:1.5;color:var(--ink-2);
}
.decide-opt input{margin:.25rem 0 0;accent-color:var(--accent)}
.decide-opt:hover span{color:var(--ink)}
.decide-out{
  border-right:1px solid var(--line);border-bottom:1px solid var(--line);
  background:rgba(255,255,255,.05);padding:1.15rem 1.3rem 1.25rem;
}
.decide-out .lbl{
  font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;text-transform:uppercase;
  color:var(--accent-ink);font-weight:400;display:block;margin-bottom:.4rem;
}
.decide-out p{margin:0;font-size:15px;line-height:1.58;color:var(--ink-2)}
@media (min-width:760px){
  .decide{display:grid;grid-template-columns:repeat(2,1fr)}
  .decide-out{grid-column:1/-1}
}

/* ---------- differences sort ---------- */
.cmp-sort{
  display:block;margin-top:.5rem;
  background:transparent;border:1px solid var(--line-cell);border-radius:0;
  color:var(--muted);font-family:var(--mono);font-size:var(--t-11);letter-spacing:.04em;
  text-transform:uppercase;padding:.3rem .55rem;cursor:pointer;
}
.cmp-sort:hover{color:var(--accent);border-color:rgba(165,204,253,.4)}
.cmp-sort[aria-pressed="true"]{color:var(--accent);border-color:rgba(165,204,253,.5)}
.cmp-sort:focus-visible{outline:2px solid var(--accent);outline-offset:1px}

/* ============================================================
   Frontal site chrome (GRAFIT), copied from Frontal-so/frontal-website
   ------------------------------------------------------------
   The nav, the footer, the floating phone booking button, the booking
   bar, the Frontal Five sign-up and the button, exactly as the site sets
   them (Alex, 19 Oct 2026: the directory carries the site's own chrome).
   Sources, all on the site's GRAFIT build:
     public/assets/grafit/components.css   .gf-button
     src/styles/grafit.css                 tokens, .gf-shell, .gf-skip, .gf-eyebrow
     src/styles/grafit-nav.css             the nav and its phone menu
     src/components/grafit/*.astro <style> Footer, FloatingBookingCTA,
                                           BookingForm, NewsletterForm
     src/styles/grafit-pointer.css         the mouse layer: sliding highlight,
                                           hover easing, the operator arrow
   Changes from the originals, and only these: the nav's own font-face
   names map to this file's families, the backdrop blur carries its
   -webkit- twin, and the mouse layer's rules for graphics the directory
   does not have are left out. The nav and footer were re-copied at the
   site's 833272e (Alex, 20 Oct: Resources and About menus, the 16px row
   glyphs, the plays library card). When the site changes its chrome,
   re-copy from those files.
   ============================================================ */

/* The site's metric-matched fallback faces (grafit.css: Inter Fallback on Arial and so on)
   are left out on purpose: build/export-frontal.py maps every @font-face here to a file in
   /fonts/grafit and refuses one it cannot map, and a local() face has no file. The stacks
   below still name them, so they apply wherever the site's own CSS has defined them. */

:root{
  --gf-white:#ffffff;--gf-gray-400:#9a9ca3;--gf-gray-500:#717379;--gf-gray-700:#34353a;--gf-gray-900:#0d0f15;
  --gf-dark:#04060c;--gf-blue-300:#a5ccfd;--gf-blue-700:#2c55a3;
  --gf-bg:#04060c;--gf-blue:var(--gf-blue-300);
  --gf-ease-out:cubic-bezier(.23,1,.32,1);--gf-ease-ui:cubic-bezier(.22,.61,.36,1);--gf-ease-hover:cubic-bezier(.4,0,.2,1);
  --gf-ease-hl-move:cubic-bezier(.2,.78,.16,1);--gf-ease-hl:cubic-bezier(.22,.72,.16,1);
  --gf-sans:Inter,'Inter Fallback',sans-serif;--gf-mono:'JetBrains Mono','JetBrains Mono Fallback',monospace;--gf-serif:'Season Serif','Season Serif Fallback',serif;
  --gf-line:rgba(255,255,255,.12);--gf-muted:rgba(255,255,255,.64);--gf-shell:1280px;--gf-gutter:clamp(24px,5.5556vw,80px);
}
@media(max-width:700px){:root{--gf-gutter:20px}}

.gf-shell{width:calc(100% - var(--gf-gutter)*2);max-width:var(--gf-shell);margin-inline:auto;border-inline:1px solid var(--gf-line)}
.gf-eyebrow{font:400 11px/1.4 var(--gf-mono);text-transform:uppercase;letter-spacing:.04em}
@media(max-width:700px){.gf-eyebrow{font-size:12px}}
.gf-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}
.gf-skip{position:fixed;z-index:100;left:16px;top:12px;padding:12px 18px;background:#fff;color:#04060c!important;font:400 15px/1.4 var(--gf-sans);text-decoration:none;transform:translateY(-150%)}
.gf-skip:focus{transform:translateY(0)}

/* ---------- the button: components.css at the site's 6ab6157, verbatim (the touch tap state, the parked
   reveal and the light secondary included), except the sweep property is --gd-button-sweep: the site
   allows one --button-sweep setter (its own components.css), and the value stays its #a5ccfd ---------- */
.gf-button.gf-button[data-gf-theme]{--button-fill:#ffffff;--button-ink:#04060c;--button-ink-swept:#04060c;--button-focus:#a5ccfd;--gd-button-sweep:#a5ccfd;--button-corner:#ffffff;--button-corner-size:6.5px;--button-corner-offset:-.5px;--button-inset:4px;--button-pad-x:24px;--button-ease:var(--gf-ease-out,cubic-bezier(.23,1,.32,1));position:relative;isolation:isolate;display:inline-flex;align-items:center;justify-content:center;gap:0;box-sizing:border-box;max-width:100%;height:auto;min-height:44px;padding:8px var(--button-pad-x);border:0;border-radius:0;background:linear-gradient(var(--button-fill),var(--button-fill)) center / calc(100% - 2 * var(--button-inset)) calc(100% - 2 * var(--button-inset)) no-repeat;color:var(--button-ink);font:400 13px/20px var(--gf-mono,'JetBrains Mono',monospace);letter-spacing:.015em;text-align:center;text-transform:uppercase;text-decoration:none;white-space:normal;overflow:visible;box-shadow:none;clip-path:none;filter:none;transform:none;transform-origin:center;cursor:pointer;-webkit-tap-highlight-color:transparent;transition:scale 140ms var(--button-ease)}
.gf-button.gf-button[data-gf-theme='light']{--button-fill:#04060c;--button-ink:#ffffff;--button-focus:#2c55a3;--button-corner:#04060c}
.gf-button.gf-button[data-gf-variant='secondary']{--button-fill:rgba(255,255,255,.05);--button-ink:#ffffff;--button-ink-swept:var(--button-ink);--button-corner:#d9d9d9;--button-corner-size:6px;--button-corner-offset:4px}
.gf-button.gf-button[data-gf-variant='secondary'][data-gf-theme='light']{--button-fill:rgba(4,6,12,.05);--button-ink:#04060c;--button-corner:#34353a}
.gf-button.gf-button[data-gf-theme]::before,.gf-button.gf-button[data-gf-theme]::after{content:'';position:absolute;z-index:-1;inset:var(--button-inset);width:auto;height:auto;margin:0;padding:0;border:0;border-radius:0;opacity:1;pointer-events:none;transform:none;translate:none;scale:none}
.gf-button.gf-button[data-gf-theme]::before{background:var(--gd-button-sweep);clip-path:inset(0 100% 0 0);transition:clip-path 170ms var(--button-ease);inset:calc(var(--button-inset) - .5px)}
.gf-button.gf-button[data-gf-variant='secondary']::before{content:none}
@supports (background-clip: text) or (-webkit-background-clip: text){
  .gf-button.gf-button[data-gf-theme] .gf-button__label{flex:1 0 auto;margin-inline:calc(-1 * var(--button-pad-x));padding-inline:var(--button-pad-x);--button-label-rest:calc(var(--button-inset) - .5px + 100%);--button-label-swept:calc(.5px - var(--button-inset));background:linear-gradient(90deg,var(--button-ink-swept) 50%,var(--button-ink) 50%) var(--button-label-rest) 0 / 200% 100% no-repeat;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;transition:background-position var(--button-label-duration,170ms) var(--button-ease)}
}
.gf-button.gf-button[data-gf-theme].gf-is-tapped .gf-button__label{background-position-x:var(--button-label-swept);--button-label-duration:180ms}
.gf-button.gf-button[data-gf-theme]:not([aria-disabled='true']):focus-visible .gf-button__label,.gf-button.gf-button[data-gf-theme][aria-busy='true'] .gf-button__label,[aria-busy='true'] .gf-button.gf-button[data-gf-theme] .gf-button__label{background-position-x:var(--button-label-swept)}
.gf-button.gf-button[data-gf-theme]::after{z-index:0;inset:var(--button-corner-offset);background:linear-gradient(var(--button-corner),var(--button-corner)) left top / var(--button-corner-size) 1px no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) left top / 1px var(--button-corner-size) no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) left bottom / var(--button-corner-size) 1px no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) left bottom / 1px var(--button-corner-size) no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) right top / var(--button-corner-size) 1px no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) right top / 1px var(--button-corner-size) no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) right bottom / var(--button-corner-size) 1px no-repeat,linear-gradient(var(--button-corner),var(--button-corner)) right bottom / 1px var(--button-corner-size) no-repeat;transition:inset 260ms var(--button-ease)}
@media (hover:hover) and (pointer:fine){
  .gf-button.gf-button[data-gf-theme]:not(:disabled):not([aria-disabled='true']):hover::before,:any-link:hover .gf-button.gf-button[data-gf-theme]::before{clip-path:inset(0);transition-duration:320ms}
  .gf-button.gf-button[data-gf-theme]:not(:disabled):not([aria-disabled='true']):hover .gf-button__label,:any-link:hover .gf-button.gf-button[data-gf-theme] .gf-button__label{background-position-x:var(--button-label-swept);--button-label-duration:320ms}
  .gf-button.gf-button[data-gf-theme]:not(:disabled):not([aria-disabled='true']):hover::after,:any-link:hover .gf-button.gf-button[data-gf-theme]::after{inset:calc(var(--button-corner-offset) - 5px)}
  .gf-button.gf-button[data-gf-theme]:not(:disabled):not([aria-disabled='true']):active::after,:any-link:active .gf-button.gf-button[data-gf-theme]::after{inset:calc(var(--button-corner-offset) - 2px)}
}
@media (prefers-reduced-motion:no-preference) and (hover:hover) and (pointer:fine){.gf-button.gf-button[data-gf-theme]:not(:disabled):not([aria-disabled='true']):active,:any-link:active .gf-button.gf-button[data-gf-theme]{scale:.97}}
@media (prefers-reduced-motion:no-preference){.gf-button.gf-button[data-gf-theme].gf-is-tapped{scale:.97}}
@media (prefers-reduced-motion:no-preference) and (hover:none),(prefers-reduced-motion:no-preference) and (pointer:coarse){
  .gf-button.gf-button[data-gf-theme][data-gf-reveal='armed']::after{inset:calc(var(--button-corner-offset) - 5px)}
  .gf-button.gf-button[data-gf-theme][data-gf-reveal='play']::after{animation:gf-reveal-brackets 600ms var(--button-ease) var(--gf-reveal-delay,0ms) both}
  .gf-button.gf-button[data-gf-theme][data-gf-reveal='play']::before{animation:gf-reveal-sweep 600ms linear var(--gf-reveal-delay,0ms) both}
  .gf-button.gf-button[data-gf-theme][data-gf-reveal='play'] .gf-button__label{animation:gf-reveal-label 600ms linear var(--gf-reveal-delay,0ms) both}
}
@keyframes gf-reveal-brackets{from{inset:calc(var(--button-corner-offset) - 5px)}}
@keyframes gf-reveal-sweep{0%{clip-path:inset(0 100% 0 0);animation-timing-function:cubic-bezier(.23,1,.32,1)}50%{clip-path:inset(0);animation-timing-function:cubic-bezier(.55,0,.45,1)}100%{clip-path:inset(0 100% 0 0)}}
@keyframes gf-reveal-label{0%{background-position-x:var(--button-label-rest);animation-timing-function:cubic-bezier(.23,1,.32,1)}50%{background-position-x:var(--button-label-swept);animation-timing-function:cubic-bezier(.55,0,.45,1)}100%{background-position-x:var(--button-label-rest)}}
.gf-button.gf-button[data-gf-theme].gf-is-tapped::before{clip-path:inset(0);transition-duration:180ms}
.gf-button.gf-button[data-gf-theme].gf-is-tapped::after{inset:calc(var(--button-corner-offset) + 2px);transition-duration:140ms}
:any-link:has(.gf-button[data-gf-theme]){-webkit-tap-highlight-color:transparent}
.gf-button.gf-button[data-gf-theme]:focus-visible{outline:2px solid var(--button-focus);outline-offset:6px}
.gf-button.gf-button[data-gf-theme]:not([aria-disabled='true']):focus-visible::before{clip-path:inset(0)}
.gf-button.gf-button[data-gf-theme]:disabled,.gf-button.gf-button[data-gf-theme][aria-disabled='true']{opacity:.4;cursor:not-allowed}
.gf-button.gf-button[data-gf-theme][aria-busy='true']{cursor:wait}
.gf-button.gf-button[data-gf-theme][aria-busy='true']::before,[aria-busy='true'] .gf-button.gf-button[data-gf-theme]::before{clip-path:inset(0)}
[aria-busy='true'] .gf-button.gf-button[data-gf-theme]:disabled{opacity:1;cursor:wait}
.gf-button.gf-button[data-gf-theme][hidden]{display:none !important}
@media (prefers-reduced-motion:reduce){
  .gf-button.gf-button[data-gf-theme],.gf-button.gf-button[data-gf-theme]::before,.gf-button.gf-button[data-gf-theme]::after,.gf-button.gf-button[data-gf-theme] .gf-button__label{transition-duration:0s !important;transition-delay:0s !important}
}
@media (forced-colors:active){
  .gf-button.gf-button[data-gf-theme]{border:1px solid ButtonText;background:ButtonFace;color:ButtonText}
  .gf-button.gf-button[data-gf-theme]::before,.gf-button.gf-button[data-gf-theme]::after{display:none}
  .gf-button.gf-button[data-gf-theme]:focus-visible{outline-color:Highlight}
}
@media print,(forced-colors:active){.gf-button.gf-button[data-gf-theme] .gf-button__label{background:none;-webkit-text-fill-color:currentColor}}

/* ---------- the nav: grafit-nav.css at 833272e (Alex, 20 Oct: Services, Our Work, Resources, About) ---------- */
/* Scoped independently of both the editorial and marketing page resets. */
.gf-nav{--nav-bg:#04060c;--nav-blue:#a5ccfd;--nav-gutter:clamp(24px,5.5556vw,80px);height:64px;position:relative;z-index:50;background:var(--nav-bg);color:#fff;border-bottom:1px solid #ffffff1a;font:400 14px/1.4 var(--gf-sans);letter-spacing:-.015em;text-align:left;color-scheme:dark;-webkit-font-smoothing:antialiased}
.gf-nav,.gf-nav *,.gf-nav *::before,.gf-nav *::after{box-sizing:border-box}
.gf-nav a{color:inherit;text-decoration:none}
.gf-nav img{display:block;max-width:none;height:auto}
.gf-nav :focus-visible{outline:2px solid var(--nav-blue);outline-offset:4px}
.gf-nav p{margin:0}
.gf-nav summary{list-style:none;cursor:pointer}
.gf-nav summary::-webkit-details-marker{display:none}
.gf-nav summary::marker{content:''}
.gf-service-menu:not([open])>.gf-service-dropdown,.gf-mobile-menu:not([open])>.gf-mobile-panel,.gf-mobile-services:not([open])>.gf-mobile-service-list{display:none}
.gf-nav::before{content:'';position:fixed;inset:0 0 auto;height:64px;background:var(--nav-bg);border-bottom:1px solid #ffffff1a;pointer-events:none;transition:opacity 200ms ease}
.gf-nav-inner{position:fixed;top:0;left:0;right:0;z-index:1;height:63px;max-width:1280px;width:calc(100% - var(--nav-gutter)*2);margin-inline:auto;display:flex;align-items:center;justify-content:space-between;gap:24px;pointer-events:none}
.gf-nav-inner>*{pointer-events:auto}
.gf-nav-brand{position:relative;width:91px;height:18px;flex:none}
.gf-nav-brand::after{content:'';position:absolute;inset:-13px -4px}
.gf-nav-mark{position:absolute;left:1.44px;top:1.64px}
.gf-nav-word{position:absolute;left:27.3px;top:1.63px;transition:opacity 180ms ease}
.gf-desktop-nav{position:absolute;left:50%;top:0;translate:-50% 0;height:63px;display:flex;align-items:center;gap:24px;white-space:nowrap}
.gf-service-menu>summary{display:flex;align-items:center;min-height:44px}
/* Hover colours sit behind the mouse gate so a tap on a touch screen never leaves one stuck on. */
@media(hover:hover) and (pointer:fine){.gf-service-menu>summary:hover{color:var(--nav-blue)}}
.gf-service-menu{position:relative;height:64px;display:flex;align-items:center}
.gf-service-menu>summary{height:100%;gap:6px}
.gf-nav-chevron{transform:rotate(180deg)}
.gf-service-menu[open] .gf-nav-chevron{transform:none}
.gf-service-dropdown{position:absolute;left:50%;top:71px;transform:translateX(calc(-50% + var(--gf-dropdown-offset,0px) + var(--gf-dropdown-shift,0px)));width:384px;max-height:calc(100dvh - 87px);overflow-y:auto;overscroll-behavior:contain;padding:4px;border:1px solid #ffffff26;background:var(--nav-bg);white-space:normal}
.gf-nav-service{display:grid;grid-template-columns:32px minmax(0,1fr);gap:5px 14px;padding:14px;font-size:15px;line-height:1.4;font-weight:400;letter-spacing:-.015em}
.gf-nav-service>span{grid-column:2;min-width:0}
/* Menu row icons (Alex, 20 Oct, after the GRAFIT menus): a 16px monochrome line glyph in a 32px square hairline
   tile, muted at rest, at the row's full ink on hover, focus and the current page. The tile sits 8px from the top
   of every row, level with the label's first line. */
.gf-nav-service-icon{display:block;grid-column:1;grid-row:1 / span 2;width:32px;height:32px;margin-top:-6px;padding:7px;border:1px solid #ffffff1f;background:#ffffff08;color:#ffffffa3}
.gf-service-dropdown .gf-nav-service+.gf-nav-service{margin-top:2px}
.gf-nav-service[aria-current]{background:#ffffff1a}
.gf-nav-service:is([aria-current],:focus-visible) .gf-nav-service-icon{border-color:#ffffff3d;color:#fff}
@media(hover:hover) and (pointer:fine){.gf-nav-service:hover{background:#ffffff1a}.gf-nav-service:hover .gf-nav-service-icon{border-color:#ffffff3d;color:#fff}}
.gf-nav-service:focus-visible{outline-offset:-2px;background:#ffffff1a}
.gf-nav-description{color:#fff9}
.gf-nav-all-services{min-height:44px;border-bottom:1px solid #ffffff1a}
.gf-nav-shade{display:none;position:fixed;inset:64px 0 0;background:#0005}
/* The Services panel keeps the place it had before Our Work, in both nav
   states. Our Work made the link row 32.58px wider than the Results link did;
   Resources and About in place of How it works, Team and Blog (20 Oct) made it
   29.56px narrower again, so it is 3.02px wider than before Our Work and
   Services sits 1.51px left of that place. */
#gf-desktop-services{--gf-dropdown-offset:1.51px}
/* Every panel stays at least 16px inside the viewport: src/scripts/grafit-nav.ts
   measures an open panel and sets --gf-dropdown-shift when it would cross. */
/* Our Work (and Resources): the Services dropdown mechanism (native details,
   same open, close and keyboard behaviour), with a panel of columns centred on
   the whole nav. */
.gf-work-menu{position:static}
.gf-work-panel{display:grid;grid-template-columns:minmax(0,220px) minmax(0,1fr);width:min(720px,calc(100vw - 48px));padding:0}
.gf-work-panel__links,.gf-work-panel__featured{display:flex;flex-direction:column;min-width:0;padding:4px}
.gf-work-panel__links{border-right:1px solid #ffffff1a}
.gf-work-panel__label{margin:0;padding:10px 14px 8px;color:#fff9;font:400 11px/16px var(--gf-mono);letter-spacing:.04em;text-transform:uppercase}
/* One-line rows (All services, Our Work, Resources, About): 48px, the tile centred. */
.gf-work-row,.gf-nav-all-services{align-items:center;padding-block:8px}
:is(.gf-work-row,.gf-nav-all-services) .gf-nav-service-icon{grid-row:1;margin-top:0}
.gf-work-feature{flex:1;display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);min-height:148px;border:1px solid #a5ccfd33;background:radial-gradient(120% 140% at 100% 0%,#a5ccfd2e,#a5ccfd00 62%),radial-gradient(90% 120% at 0% 100%,#58597d40,#58597d00 70%),#0a0d16}
.gf-work-feature[aria-current]{border-color:#a5ccfd8c}
@media(hover:hover) and (pointer:fine){.gf-work-feature:hover{border-color:#a5ccfd8c}}
.gf-work-feature:focus-visible{outline-offset:-2px}
.gf-work-feature__story{display:flex;flex-direction:column;justify-content:space-between;gap:20px;min-width:0;padding:18px}
.gf-work-feature__head{display:flex;flex-wrap:wrap;align-items:center;gap:10px 12px}
.gf-work-feature__tags{display:flex;flex-wrap:wrap;gap:4px}
.gf-client-logo{display:inline-flex;align-items:center}
/* ClientLogo.astro draws the wordmark as a 71x23 svg; the directory carries it as an image, held to the
   same box (contain is what the svg viewBox does), not to the 22.7px its aspect ratio would give it. */
.gf-nav img.gf-client-logo__art{width:71px;height:23px;object-fit:contain}
.gf-work-feature__tag{padding:3px 6px;border:1px solid #ffffff33;background:#ffffff0d;color:#fffc;font:400 10px/1.3 var(--gf-mono);letter-spacing:.04em;text-transform:uppercase;white-space:nowrap}
.gf-work-feature__outcome{font-size:15px;line-height:1.35;letter-spacing:-.015em}
.gf-work-feature__stats{display:grid;grid-auto-rows:1fr;min-width:0;border-left:1px solid #ffffff1f}
.gf-work-feature__stat{display:flex;flex-direction:column;justify-content:center;gap:2px;padding:12px 18px}
.gf-work-feature__stat+.gf-work-feature__stat{border-top:1px solid #ffffff1f}
.gf-work-feature__value{font-size:18px;line-height:1.2;letter-spacing:-.02em}
.gf-work-feature__stat-label{color:#fffa;font-size:12px;line-height:1.35}
/* Resources (Alex, 20 Oct): the Our Work panel with two labelled link columns, Tools and Read, and the
   featured play: its diagram cover, cropped from the top, over its title. About: a short list like Services. */
.gf-resources-panel{grid-template-columns:minmax(0,190px) minmax(0,190px) minmax(0,1fr)}
.gf-play-feature{display:flex;flex-direction:column}
.gf-play-feature__mosaic{flex:1 1 136px;min-height:0;display:grid;grid-template:1fr 1fr/1fr 1fr;gap:1px;background:var(--gf-line)}.gf-play-feature__mosaic img{width:100%;height:100%;min-height:0;object-fit:cover;object-position:top;background:#f3f4f6}.gf-play-feature__count{display:block;margin-top:4px;color:var(--gf-muted);font:400 11px/1.4 var(--gf-mono);letter-spacing:.04em;text-transform:uppercase}
.gf-play-feature__title{padding:14px 18px;font-size:15px;line-height:1.35}
.gf-about-panel{width:240px}
.gf-nav:has(.gf-service-menu[open])>.gf-nav-shade{display:block}
.gf-nav-bracket{position:relative;display:flex;align-items:center;justify-content:center;border:0;background:#ffffff0d;color:#fff}
.gf-nav-bracket::before,.gf-nav-bracket::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:.7}
.gf-nav-bracket::before{background:linear-gradient(#d9d9d9,#d9d9d9) left top/6px 1px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) right top/6px 1px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) left bottom/6px 1px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) right bottom/6px 1px no-repeat}
.gf-nav-bracket::after{background:linear-gradient(#d9d9d9,#d9d9d9) left top/1px 6px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) right top/1px 6px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) left bottom/1px 6px no-repeat,linear-gradient(#d9d9d9,#d9d9d9) right bottom/1px 6px no-repeat}
/* Button lab motion: brackets move 5px out on hover, pull in on press, press scales to .97. */
.gf-nav-bracket{transition:scale 140ms cubic-bezier(.23,1,.32,1);-webkit-tap-highlight-color:transparent}
.gf-nav-bracket::before,.gf-nav-bracket::after{transition:inset 260ms cubic-bezier(.23,1,.32,1)}
@media(hover:hover) and (pointer:fine){.gf-nav-bracket:hover::before,.gf-nav-bracket:hover::after{inset:-5px}.gf-nav-bracket:active::before,.gf-nav-bracket:active::after{inset:-2px}}
/* The :active press is for a mouse only: a touch shows it at finger down, even when the touch becomes a scroll. */
@media(prefers-reduced-motion:no-preference) and (hover:hover) and (pointer:fine){.gf-nav-bracket:active{scale:.97}}
/* Touch tap, set by src/scripts/grafit-button-motion.ts: the press, with the brackets 2px in. */
.gf-nav-bracket.gf-is-tapped::before,.gf-nav-bracket.gf-is-tapped::after{inset:2px;transition-duration:140ms}
@media(prefers-reduced-motion:no-preference){.gf-nav-bracket.gf-is-tapped{scale:.97}}
.gf-nav .gf-nav-call.gf-button{width:auto;flex:none;transition:background 180ms ease,box-shadow 180ms ease,scale 140ms cubic-bezier(.23,1,.32,1)}

/* The header's original flow height remains reserved. Only its fixed groups
   dock together, so the same behavior works inside editorial header wrappers. */
@media(min-width:761px){
  .gf-nav[data-scrolled="true"]::before{opacity:0}
  .gf-nav[data-scrolled="true"] .gf-nav-inner{justify-content:center;gap:8px}
  .gf-nav[data-scrolled="true"] .gf-nav-brand{width:44px;height:44px}
  .gf-nav[data-scrolled="true"] .gf-nav-mark{left:13px;top:14px}
  .gf-nav[data-scrolled="true"] .gf-nav-word{opacity:0;pointer-events:none}
  .gf-nav[data-scrolled="true"] .gf-nav-brand::after{inset:0}
  .gf-nav[data-scrolled="true"] .gf-desktop-nav{position:relative;left:auto;top:auto;translate:none;height:44px;padding-inline:16px}
  .gf-nav[data-scrolled="true"] .gf-service-menu{height:44px}
  .gf-nav[data-scrolled="true"] .gf-service-dropdown{top:calc(100% + 12px)}
  .gf-nav-brand::before,.gf-desktop-nav::before{content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;background:rgba(4,6,12,.92);border:1px solid #a5ccfd33;box-shadow:0 8px 24px #0003;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);opacity:0;transition:opacity 200ms ease}
  .gf-nav[data-scrolled="true"] .gf-nav-brand::before,.gf-nav[data-scrolled="true"] .gf-desktop-nav::before{opacity:1}
  .gf-nav[data-scrolled="true"] .gf-nav-call{background:rgba(4,6,12,.96);box-shadow:0 8px 24px #0003}
}

.gf-mobile-menu{display:none}
.gf-nav-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
@media(max-width:900px){.gf-desktop-nav{gap:16px}.gf-nav-inner{gap:16px}}
@media(max-width:760px){
  .gf-nav{height:63px;--nav-gutter:16px}
  .gf-nav::before{height:63px}
  .gf-nav-inner{gap:12px}
  .gf-desktop-nav,.gf-nav>.gf-nav-shade{display:none!important}
  .gf-nav-call{margin-left:auto;font-size:12px}
  .gf-mobile-menu{display:block}
  .gf-nav-toggle{width:36px;height:36px}
  .gf-nav-toggle::before{inset:-4px;background-origin:content-box;padding:4px;pointer-events:auto}
  @media(hover:hover) and (pointer:fine){.gf-nav-toggle:hover::before{inset:-9px}.gf-nav-toggle:active::before{inset:-6px}}
  .gf-nav-toggle.gf-is-tapped::before{inset:-2px}
  .gf-mobile-menu>.gf-nav-toggle>.gf-nav-when-open,.gf-mobile-services>summary>.gf-nav-when-open{display:none}
  .gf-mobile-menu[open]>.gf-nav-toggle>.gf-nav-when-closed,.gf-mobile-services[open]>summary>.gf-nav-when-closed{display:none}
  .gf-mobile-menu[open]>.gf-nav-toggle>.gf-nav-when-open,.gf-mobile-services[open]>summary>.gf-nav-when-open{display:block}
  .gf-nav:has(.gf-mobile-menu[open]) .gf-nav-inner{position:fixed;top:0;left:0;width:100%;max-width:none;height:63px;padding-inline:16px;background:var(--nav-bg);border-bottom:1px solid #ffffff1a}
  .gf-mobile-panel{position:fixed;inset:63px 0 0;height:calc(100dvh - 63px);display:flex;flex-direction:column;gap:24px;overflow-y:auto;overscroll-behavior:contain;background:var(--nav-bg);padding-bottom:max(16px,env(safe-area-inset-bottom))}
  .gf-mobile-links{flex:none}
  .gf-mobile-services>summary{min-height:64px;padding:14px 16px;display:flex;align-items:center;justify-content:space-between;font-size:20px;line-height:1.4;letter-spacing:-.015em}
  .gf-mobile-services[open]{padding-bottom:10px}
  .gf-mobile-service-list{padding-inline:16px;display:flex;flex-direction:column;gap:4px}
  .gf-mobile-service-list .gf-nav-service{padding:8px 0;row-gap:4px}
  .gf-mobile-service-list .gf-nav-description{font-size:14px}
  .gf-mobile-services+.gf-mobile-services{border-top:1px solid #ffffff1a}
  .gf-mobile-service-list .gf-work-row{min-height:44px}
  .gf-mobile-services:last-child{border-bottom:1px solid #ffffff1a}
  .gf-nav .gf-mobile-booking{position:relative;display:flex;align-items:center;justify-content:center;flex:none;min-height:44px;margin: auto 16px 0;background:#fff;color:#04060c;font:400 12px/1.4 var(--gf-mono);letter-spacing:.015em;text-transform:uppercase}
  .gf-nav-cta-left,.gf-nav-cta-right{position:absolute;top:50%;transform:translateY(-50%)}
  .gf-nav-cta-left{left:-4.5px;transform:translateY(-50%) rotate(180deg)}
  .gf-nav-cta-right{right:-4.5px}
  .gf-nav-toggle{width:44px;height:44px}
}
@media(prefers-reduced-motion:reduce){.gf-nav::before,.gf-nav-brand::before,.gf-desktop-nav::before,.gf-nav-word,.gf-nav .gf-nav-call.gf-button,.gf-nav-bracket,.gf-nav-bracket::before,.gf-nav-bracket::after{transition:none}}

/* Desktop menus (interactions spec 3.2): a panel fades in over 320ms and slides over 500ms. Switching menus by
   hover, it comes in from the side the pointer moved to and the old one leaves the other way
   (src/scripts/grafit-pointer.js sets --gf-dd-from and --gf-dd-to); opened by a click or a key, it drops 6px
   into place. The chevron turns over 300ms and the shade fades with the panel. Where the browser can animate a
   closing details element (Chromium 131 and later) the closing panel and the shade fade out over 320ms;
   elsewhere they close at once, as before. The sideways moves and the exit run at 1024px and wider only: there
   a panel never needs the edge correction src/scripts/grafit-nav.ts measures as it opens, so a slide can never
   be measured into it, and a correction dropped on close never jumps a fading panel. */
@media(prefers-reduced-motion:no-preference){
  .gf-service-menu>.gf-service-dropdown{transition:opacity 320ms var(--gf-ease-ui),translate 500ms var(--gf-ease-ui)}
  @starting-style{.gf-service-menu[open]>.gf-service-dropdown{opacity:0;translate:0 -6px}}
  .gf-nav-chevron{transition:transform 300ms var(--gf-ease-ui)}
  .gf-nav-shade{transition:opacity 320ms var(--gf-ease-ui)}
  @starting-style{.gf-nav:has(.gf-service-menu[open])>.gf-nav-shade{opacity:0}}
}
@media(prefers-reduced-motion:no-preference) and (min-width:1024px){
  @starting-style{.gf-service-menu[open]>.gf-service-dropdown{opacity:0;translate:var(--gf-dd-from,0px) -6px}}
  .gf-service-menu::details-content{transition:content-visibility 320ms allow-discrete}
  .gf-service-menu:not([open])>.gf-service-dropdown{display:none;opacity:0;translate:var(--gf-dd-to,0px) 0;pointer-events:none;transition:opacity 320ms var(--gf-ease-ui),translate 500ms var(--gf-ease-ui),display 320ms allow-discrete}
  .gf-nav-shade{transition:opacity 320ms var(--gf-ease-ui),display 320ms allow-discrete}
  .gf-nav:not(:has(.gf-service-menu[open]))>.gf-nav-shade{opacity:0}
}

/* Phone menu (interactions spec 3.8, tap driven): the background wipes down over 500ms and the rows rise into
   place one after another (500ms each, starting 250ms in, 70ms apart). Closing stays instant, and reduced
   motion opens the menu at once with its own background. */
@media(max-width:760px) and (prefers-reduced-motion:no-preference){
  .gf-mobile-menu[open]>.gf-mobile-panel{background:transparent}
  .gf-mobile-menu[open]>.gf-mobile-panel::before{content:'';position:fixed;inset:63px 0 0;z-index:-1;background:var(--nav-bg);transform-origin:top;pointer-events:none;transition:transform 500ms var(--gf-ease-ui)}
  @starting-style{.gf-mobile-menu[open]>.gf-mobile-panel::before{transform:scaleY(0)}}
  .gf-mobile-menu[open] .gf-mobile-links>*,.gf-nav .gf-mobile-menu[open] .gf-mobile-booking.gf-button{transition:opacity 500ms var(--gf-ease-ui) var(--gf-mm-delay,250ms),translate 500ms var(--gf-ease-ui) var(--gf-mm-delay,250ms),color 200ms var(--gf-ease-hover),scale 140ms var(--gf-ease-out)}
  @starting-style{.gf-mobile-menu[open] .gf-mobile-links>*,.gf-nav .gf-mobile-menu[open] .gf-mobile-booking.gf-button{opacity:0;translate:0 16px}}
  .gf-mobile-links>:nth-child(2){--gf-mm-delay:320ms}
  .gf-mobile-links>:nth-child(3){--gf-mm-delay:390ms}
  .gf-mobile-links>:nth-child(4){--gf-mm-delay:460ms}
  .gf-nav .gf-mobile-booking{--gf-mm-delay:530ms}
}
/* ---------- the mouse layer: grafit-pointer.css at 833272e, its nav and footer parts ----------
   The site loads this sheet and src/scripts/grafit-pointer.js on the first mouse move, never on a touch screen,
   without a fine pointer or with reduced motion. Here the rules sit in the stylesheet, which changes nothing: the
   highlight rules only act on .gf-hl, which the script adds, and the rest sit behind the mouse gate. app.js starts
   the script's nav half the same way the site does (initPointer). Left out because the directory has none of
   them: the glow on case-study and service graphics, the logo grid, the team rows, the service and home arrows.
   Sliding highlight: one block glides from row to row in the desktop menus; before the script loads, and with
   reduced motion, each row keeps its own tint. */
:where(.gf-hl){position:relative;isolation:isolate}
.gf-hl{--hl-bg:#a5ccfd;--hl-ink:#04060c}
.gf-hl::before{content:'';position:absolute;left:0;top:0;z-index:-1;width:100px;height:100px;pointer-events:none;background:var(--hl-bg);opacity:0;transform:translate3d(var(--hl-x,0px),var(--hl-y,0px),0) scale(var(--hl-sx,1),var(--hl-sy,1)) scale(.99);will-change:transform,opacity;transition:opacity var(--hl-od,125ms) var(--gf-ease-hl),transform var(--hl-td,230ms) var(--gf-ease-hl-move)}
.gf-hl.is-primed::before{transition:none}
.gf-hl.is-on::before{opacity:1;transform:translate3d(var(--hl-x,0px),var(--hl-y,0px),0) scale(var(--hl-sx,1),var(--hl-sy,1)) scale(1)}
.gf-hl>*,.gf-hl>* :is(a,.gf-nav-description,.gf-nav-service-icon){transition:color 220ms var(--gf-ease-hl)}
.gf-hl>.is-hl,.gf-hl>.is-hl :is(a,.gf-nav-description,.gf-nav-service-icon){transition-delay:20ms}
.gf-hl>.is-hl,.gf-hl>.is-hl :is(a,.gf-nav-service-icon){color:var(--hl-ink)}
/* A row icon's tile follows the ink onto the block: a dark hairline and a faint dark fill. */
.gf-hl>* .gf-nav-service-icon{transition:color 220ms var(--gf-ease-hl),border-color 220ms var(--gf-ease-hl),background-color 220ms var(--gf-ease-hl)}
.gf-hl>.is-hl .gf-nav-service-icon{border-color:#04060c2e;background-color:#04060c0a}
.gf-hl>.is-hl .gf-nav-description{color:rgba(4,6,12,.72)}
.gf-hl.is-live>.is-hl[aria-current]{background:transparent}
@media(hover:hover) and (pointer:fine){@media(prefers-reduced-motion:no-preference){.gf-hl.is-live>.gf-nav-service:hover,.gf-hl.is-live>li>a:hover{background:transparent}}}
/* GRAFIT's slot slide on the footer operator tile: the arrow leaves to the top right and comes back in. */
/* Hover colours ease over 200ms with a mouse. */
@media(hover:hover) and (pointer:fine){
  .gf-service-menu>summary,.gf-footer-column ul a,.gf-footer-legal a,.gf-contact small{transition:color 200ms var(--gf-ease-hover)}
  .gf-work-feature{transition:border-color 200ms var(--gf-ease-hover)}
  .gf-footer-socials a{transition:opacity 200ms var(--gf-ease-hover)}
  .gf-ai-summary a{transition:background-color 200ms var(--gf-ease-hover)}
}
@media(prefers-reduced-motion:reduce){
  .gf-hl::before{display:none}
  .gf-hl>*,.gf-hl>* :is(a,.gf-nav-description,.gf-nav-service-icon),.gf-service-menu>summary,.gf-footer-column ul a,.gf-footer-legal a,.gf-contact small,.gf-work-feature,.gf-footer-socials a,.gf-ai-summary a{transition:none}
}

/* ---------- the floating Book a call on phones: FloatingBookingCTA.astro ---------- */
.gf-floating-booking{position:fixed;right:clamp(16px,4vw,32px);bottom:max(clamp(16px,4vw,28px),env(safe-area-inset-bottom));z-index:45;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(12px);transition:opacity 220ms ease,transform 300ms cubic-bezier(.22,1,.36,1),visibility 0s linear 300ms}
.gf-floating-booking.show{opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0);transition:opacity 220ms ease,transform 300ms cubic-bezier(.22,1,.36,1)}
.gf-floating-booking__link.gf-button.gf-button[data-gf-theme]{--button-inset:0px;--button-corner-size:6px;--button-corner-offset:-5px;--button-pad-x:16px;min-height:48px;box-shadow:0 10px 30px rgba(0,0,0,.35);white-space:nowrap}
@media(max-width:760px){.gf-floating-booking{right:16px;bottom:max(16px,env(safe-area-inset-bottom))}}
[data-gf-nav]:has([data-gf-mobile-menu][open]) .gf-floating-booking,.gf-floating-booking.is-suppressed{display:none}
@media(min-width:761px){.gf-floating-booking{display:none}}
@media(prefers-reduced-motion:reduce){.gf-floating-booking,.gf-floating-booking.show{transition:none}}

/* ---------- the booking bar: BookingForm.astro, glass variant ----------
   Every booking call to action in the directory is this bar (email first). On these
   dark surfaces it is the glass bar the site uses on its own dark sections. */
.gf-booking{--booking-ink:var(--gf-dark);--booking-placeholder:var(--gf-gray-500);--booking-corner:rgba(255,255,255,.6);--booking-focus:var(--gf-blue-300);--booking-focus-ring:rgba(165,204,253,.6);--booking-focus-glow:rgba(165,204,253,.1);--booking-bad:#ff9d9d;--booking-bad-ring:rgba(255,157,157,.7);--booking-ring:0 0 0 1px #ffffff28;--booking-ring-focus:0 0 0 1px var(--booking-focus-ring),0 0 0 4px var(--booking-focus-glow);--booking-ring-error:0 0 0 1px var(--booking-bad-ring);width:400px;max-width:100%;position:relative;text-align:left}
.gf-booking-form{--booking-bracket:var(--booking-corner);position:relative;display:flex;align-items:center;gap:12px;width:100%;padding:2px 4px 2px 20px;background:#fff;color:var(--booking-ink);box-shadow:var(--booking-ring);min-height:48px;margin:0;transition:box-shadow 200ms ease}
.gf-booking-form::before,.gf-booking-form::after{content:'';position:absolute;top:-5px;bottom:-5px;width:7px;pointer-events:none;background:linear-gradient(var(--booking-bracket),var(--booking-bracket)) left top/7px 1px no-repeat,linear-gradient(var(--booking-bracket),var(--booking-bracket)) left bottom/7px 1px no-repeat,linear-gradient(var(--booking-bracket),var(--booking-bracket)) left top/1px 7px no-repeat,linear-gradient(var(--booking-bracket),var(--booking-bracket)) left bottom/1px 7px no-repeat;transition:translate 260ms var(--gf-ease-out)}
.gf-booking-form::before{left:-5px}
.gf-booking-form::after{right:-5px;transform:scaleX(-1)}
.gf-booking-form:focus-within{--booking-bracket:var(--booking-focus);box-shadow:var(--booking-ring-focus)}
.gf-booking-form:focus-within::before{translate:-3px 0}
.gf-booking-form:focus-within::after{translate:3px 0}
.gf-booking-form[data-state='error']{--booking-bracket:var(--booking-bad);box-shadow:var(--booking-ring-error)}
.gf-booking-form[data-state='busy']{cursor:progress}
.gf-booking input{border:0;outline:0;background:transparent;padding:8px 0;min-width:0;min-height:44px;flex:1;font:400 14px/1.4 var(--gf-sans);letter-spacing:-.015em;color:var(--booking-ink);border-radius:0}
.gf-booking input::placeholder{color:var(--booking-placeholder)}
.gf-booking .gf-button.gf-button[data-gf-theme]{flex:none;min-width:150px;white-space:nowrap}
.gf-booking .gf-button.gf-button[data-gf-theme]::after{display:none}
.gf-booking-form[data-state='busy'] .gf-button.gf-button[data-gf-theme]{cursor:progress}
.gf-booking-form[data-state='busy'] .gf-button.gf-button[data-gf-theme]::before{clip-path:inset(0)}
.gf-booking .gf-button.gf-button[data-gf-theme] :is(.gf-booking-label,.gf-booking-busy){transition:background-position var(--button-label-duration,170ms) var(--button-ease),filter 180ms ease,opacity 180ms ease}
.gf-booking .gf-button.gf-button[data-gf-theme] .gf-booking-busy{margin:0;padding:0}
.gf-booking-form[data-state='busy'] .gf-button.gf-button[data-gf-theme] .gf-button__label{background-position-x:var(--button-label-swept)}
.gf-booking-busy{position:absolute;inset:0;display:grid;place-items:center;opacity:0;filter:blur(2px);pointer-events:none}
.gf-booking-form[data-state='busy'] .gf-booking-label{filter:blur(2px);opacity:0}
.gf-booking-form[data-state='busy'] .gf-booking-busy{filter:none;opacity:1}
.gf-booking-error{position:absolute;top:calc(100% + 10px);left:0;max-width:100%;margin:0;color:var(--booking-bad);font:400 13px/20px var(--gf-sans);letter-spacing:-.015em}
.gf-booking--glass{--booking-ink:#fff;--booking-placeholder:rgba(255,255,255,.5);--booking-glass:rgba(255,255,255,.1);--booking-ring:inset 0 0 0 1px rgba(255,255,255,.2);--booking-ring-focus:inset 0 0 0 1px var(--booking-focus-ring),0 0 0 4px var(--booking-focus-glow);--booking-ring-error:inset 0 0 0 1px var(--booking-bad-ring);width:402px}
.gf-booking--glass .gf-booking-form{box-sizing:border-box;height:44px;min-height:44px;padding:4px 4px 4px 16px;background:var(--booking-glass)}
.gf-booking--glass input{min-height:44px;height:44px;margin-block:-4px;padding:0;font-size:14px}
.gf-booking--glass .gf-button.gf-button[data-gf-theme]{--button-inset:0px;--button-pad-x:16px;min-height:36px;height:36px;padding:0 var(--button-pad-x);font:400 13px/1 var(--gf-mono);letter-spacing:.195px}
@media(hover:none) and (pointer:coarse){.gf-booking.gf-booking--glass .gf-booking-form .gf-button.gf-button.gf-button[data-gf-theme]::after{display:block;inset:-4px -4px -4px 0;background:none;pointer-events:auto;transition:none;animation:none;scale:none}}
@media(max-width:1024px){.gf-booking input{font-size:16px}}
@media(hover:none),(pointer:coarse){.gf-booking input{font-size:16px!important}}
@media(max-width:480px){.gf-booking-form{padding-left:12px;gap:6px}}
@media(max-width:420px){.gf-booking .gf-button.gf-button[data-gf-theme]{min-width:124px}.gf-booking--glass .gf-button.gf-button[data-gf-theme]{--button-pad-x:12px;padding:0 var(--button-pad-x)}.gf-booking-busy{font-size:11px}}
@media(max-width:700px){.gf-booking .gf-button.gf-button[data-gf-theme]{min-height:48px}.gf-booking--glass .gf-booking-form{height:56px;min-height:56px}.gf-booking--glass input{height:56px;min-height:56px}.gf-booking--glass .gf-button.gf-button[data-gf-theme]{min-height:48px;height:48px}}
@media(prefers-reduced-motion:reduce){.gf-booking-form,.gf-booking-form::before,.gf-booking-form::after,.gf-booking .gf-button.gf-button[data-gf-theme] :is(.gf-booking-label,.gf-booking-busy){transition:none}}

/* ---------- the footer: Footer.astro at the launch's 289402c ---------- */
.gf-footer{font:400 15px/1.4 var(--gf-sans);letter-spacing:-.015em}
.gf-footer :where(h2,p,ul,li){margin:0;padding:0;list-style:none}
.gf-footer :where(a){text-decoration:none;color:inherit}
.gf-footer :where(img,svg){display:block;max-width:100%;height:auto}
.gf-footer :focus-visible{outline:2px solid var(--gf-blue);outline-offset:4px}
.gf-footer{color:#fff;background:var(--gf-bg)}.gf-footer>.gf-shell{position:relative;overflow:hidden;min-height:751px;padding:113px 39px 0}
.gf-footer>.gf-shell::before{content:'';position:absolute;left:0;right:0;top:0;height:56px;background:repeating-linear-gradient(0deg,#ffffff18 0 1px,transparent 1px 4px);-webkit-mask-image:linear-gradient(#000,transparent);mask-image:linear-gradient(#000,transparent)}
.gf-footer-top{display:grid;grid-template-columns:1fr 1fr;gap:40px}.gf-footer-company{max-width:260px}.gf-footer-brand{display:block;position:relative;width:121.333px;height:24px;margin-bottom:20px}.gf-footer-brand .mark{position:absolute;left:1.92px;top:2.19px}.gf-footer-brand .word{position:absolute;left:36.41px;top:2.17px}
/* Link hovers sit behind the mouse gate, in place, so a tap on a phone never leaves one stuck on and a mouse sees the same cascade. */
.gf-footer-company p{color:#97989f;max-width:252px;font-size:15px;line-height:1.4}.gf-footer-socials{display:flex;gap:12px;margin-top:24px}.gf-footer-socials a{display:grid;place-items:center;min-width:24px;min-height:24px;opacity:.6}@media(hover:hover) and (pointer:fine){.gf-footer-socials a:hover{opacity:1}}
.gf-footer-columns{display:grid;grid-template-columns:1fr 1.05fr .8fr;gap:26px}.gf-footer .gf-footer-heading{display:block;margin:0 0 16px;font:400 var(--gf-footer-heading-size,13px)/1.4 var(--gf-mono);letter-spacing:.025em;text-transform:uppercase;color:#fff}.gf-footer-column ul{display:grid;gap:12px}.gf-footer-column ul a{font-size:15px;color:#9b9ca3}@media(hover:hover) and (pointer:fine){.gf-footer-column ul a:hover{color:#fff}}
.gf-footer-middle{display:grid;grid-template-columns:1fr 1fr;gap:40px;margin-top:63px;align-items:start}
.gf-ai-summary ul{display:flex;gap:8px}.gf-ai-summary a{display:grid;width:48px;height:48px;place-items:center;background:#ffffff0d}@media(hover:hover) and (pointer:fine){.gf-ai-summary a:hover{background:#ffffff20}}.gf-ai-summary img{object-fit:contain}
.gf-footer-legal{display:grid;grid-template-columns:1fr 1fr;gap:40px;margin-top:50px;color:#8b8c94;font-size:15px}.gf-footer-legal>nav{display:flex;flex-wrap:wrap;gap:26px}@media(hover:hover) and (pointer:fine){.gf-footer-legal a:hover{color:#fff}}.gf-footer-motion{margin:64px -39px 0;height:224px;overflow:hidden}
/* the settled frame the band ships with, at the scene's own size (.ft-logo: full width up to 1280px) */
.gf-footer-motion img{width:100%;height:auto;max-width:1280px}
@media(min-width:1051px){.gf-footer>.gf-shell{height:751px}.gf-footer-motion{position:absolute;right:-39px;bottom:0;left:-39px;height:188px;margin:0}}
@media(max-width:1050px){.gf-footer{--gf-footer-heading-size:11px}.gf-footer-columns{gap:18px}.gf-footer-column ul a{font-size:13px}.gf-footer-top,.gf-footer-middle,.gf-footer-legal{grid-template-columns:.85fr 1.15fr;gap:24px}}
@media(max-width:700px){.gf-footer{--gf-footer-heading-size:12px}.gf-footer>.gf-shell{padding:72px 20px 0;min-height:auto}.gf-footer-top{display:flex;flex-direction:column;gap:42px}.gf-footer-company{max-width:260px}.gf-footer-company p{font-size:16px}.gf-footer-brand{min-height:44px;margin-bottom:0}.gf-footer-columns{grid-template-columns:1fr 1.1fr;gap:36px 20px}.gf-footer-column>.gf-footer-heading{display:flex;align-items:center;min-height:44px}.gf-footer-column ul a{display:flex;align-items:center;min-height:44px;font-size:16px}.gf-footer-column ul{gap:2px}.gf-footer-middle{display:flex;flex-direction:column;align-items:stretch;gap:40px;margin-top:40px}.gf-ai-summary a{width:44px;height:44px}.gf-footer-socials a{min-width:44px;min-height:44px}.gf-footer-socials{gap:4px;margin-left:-6px}.gf-footer-legal{display:flex;flex-direction:column;gap:18px;margin-top:42px;font-size:12px}.gf-footer-legal>nav{gap:0 18px}.gf-footer-legal a{display:flex;align-items:center;min-width:44px;min-height:44px}.gf-footer-motion{height:124px;margin:36px -20px 0}}
.gf-footer .gf-footer-contact ul{display:grid;gap:8px}
.gf-footer .gf-footer-contact .gf-contact{display:grid;grid-template-columns:40px max-content;align-items:center;gap:12px;min-height:40px;color:#fff;font-size:15px}
.gf-contact-photo{width:40px;height:40px;border-radius:2px;object-fit:cover}
.gf-contact-icon{display:grid;place-items:center;width:40px;height:40px;background:#ffffff0d;color:#fff;transition:background-color 160ms ease}
/* The nav line glyph (the newsletter envelope), at the nav glyph size. */
.gf-contact-icon svg{width:16px;height:16px}
.gf-contact-copy strong{display:flex;align-items:center;gap:6px;font-weight:400;font-size:15px;line-height:19px;color:#fff;white-space:nowrap}
.gf-contact-copy small{display:block;margin-top:2px;font-size:13px;line-height:17px;color:#97989f}
.gf-footer .gf-contact-verified{flex:none;width:14px;height:14px}
.gf-contact-verified__seal{fill:var(--gf-blue)}
.gf-contact-verified__check{fill:none;stroke:var(--gf-bg);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
@media(hover:hover) and (pointer:fine){.gf-contact:hover .gf-contact-icon{background:#ffffff20}.gf-contact:hover small{color:#c4c5cb}}
@media(min-width:1241px){.gf-footer-top,.gf-footer-middle,.gf-footer-legal{grid-template-columns:minmax(0,.72fr) minmax(0,1.28fr)}.gf-footer-columns{grid-template-columns:repeat(4,max-content);justify-content:space-between;gap:24px}}
@media(max-width:1240px){.gf-footer-contact{grid-column:1/-1;margin-top:12px}.gf-footer .gf-footer-contact ul{grid-template-columns:repeat(2,max-content);gap:16px 48px}}
@media(min-width:1051px) and (max-width:1240px){.gf-footer>.gf-shell{height:auto;min-height:751px;padding-bottom:236px}}
@media(max-width:700px){.gf-footer .gf-footer-contact ul{grid-template-columns:1fr;gap:8px}.gf-footer .gf-footer-contact .gf-contact{min-height:48px;font-size:16px}.gf-contact-copy strong{font-size:16px}}
@media(prefers-reduced-motion:reduce){.gf-contact-icon{transition:none}}
/* The phone wordmark scene is fluid (372 x 78): the band takes its height, so no empty strip sits below it. Until
   footerWordmark() swaps the scene in, the band holds the settled desktop frame at its own 1280 x 267. */
@media(max-width:770px){.gf-footer .gf-footer-motion{height:auto;aspect-ratio:1280/267}.gf-footer .gf-footer-motion--m{aspect-ratio:372/78}}
/* 701 to 860px: the contacts sit in the 320px right column, too narrow for two: stack them instead of running off the edge. */
@media(min-width:701px) and (max-width:860px){.gf-footer .gf-footer-contact ul{grid-template-columns:minmax(0,1fr);gap:8px}}
/* Narrow phones: the AI summary icons wrap instead of spilling past the content edge. */
.gf-ai-summary ul{flex-wrap:wrap}
/* The footer sign-up blocks, a pair: the newsletter under the socials (copy in src/data/newsletter.ts) and Talk
   with an operator in the middle row (Alex, 20 Oct). Each block is a footer heading, an optional line in the
   company line's type, then a glass bar, 320px wide or the column's width when that is narrower (all of it on
   phones). The company column lets it run to 320px; the brand, line and socials keep their own widths. */
.gf-footer .gf-footer-company{max-width:none;min-width:0}
.gf-footer-signup{width:min(320px,100%)}
.gf-footer-newsletter{margin-top:40px}
/* The operator block is a grid item: a width that follows its column (min() above) and min-width:0, like the
   company column, keep its track the company column's width, so the two bars match from 701 to 1050px too. */
.gf-footer-operator{min-width:0}
.gf-footer .gf-footer-signup .gf-footer-heading{margin-bottom:16px}
/* The line sits 8px under the heading and 16px over the bar, in the company line's type. */
.gf-footer .gf-footer-signup__line{margin:-8px 0 16px;color:#97989f;font-size:15px;line-height:1.4;text-wrap:pretty}
/* Both bars fill the block, so the newsletter and the booking bar are one width at every size. */
.gf-footer-signup .gf-newsletter,.gf-footer-signup .gf-booking{width:100%;max-width:none}
/* 701 to 1050px the column runs down to 247px: the booking button takes the 128px that still holds "Opening
   calendar" (BookingForm.astro), so the field keeps room for "Work email". */
@media(min-width:701px) and (max-width:1050px){.gf-footer .gf-footer-signup .gf-booking .gf-button.gf-button[data-gf-theme]{min-width:128px}}
/* The sign-up makes the company column the tallest at 1051px and up: the shell grows with its content and keeps
   the wordmark band clear below the legal row, as it already did from 1051 to 1240px. */
@media(min-width:1051px){.gf-footer>.gf-shell{height:auto;min-height:751px;padding-bottom:236px}}
@media(max-width:700px){.gf-footer-signup{width:auto}.gf-footer-newsletter{margin-top:32px}.gf-footer .gf-footer-signup .gf-footer-heading{min-height:0}.gf-footer .gf-footer-signup__line{font-size:16px}}
/* The wordmark scene's own rules (SharedFooterLogoDesktop and SharedFooterLogoMobile, as the site builds them at
   289402c). The site's runtime marks its <frontal-motion> running; footerWordmark() marks the band. */
.ft-logo{--ease:cubic-bezier(.16, 1, .3, 1);--draw:cubic-bezier(.45, 0, .2, 1);aspect-ratio:1280/267;background:#04060c;width:100%;max-width:1280px;position:relative;overflow:hidden}
.ft-logo svg{width:100%;height:100%;display:block;position:absolute;inset:0}
.ft-logo .lg{fill:none;stroke-width:1px;stroke-dasharray:1;stroke-dashoffset:1px}
.ft-logo{--sweep:cubic-bezier(.6, 0, .25, 1)}
.ft-logo .rv,.ft-logo .band{transform:translate(0)}
.ft-logo.is-in .lg{stroke-dashoffset:0;transition:stroke-dashoffset 1.6s var(--draw) calc(var(--i) * 90ms)}
.ft-logo.is-in .rv,.ft-logo.is-in .band{transition:transform 1.6s var(--sweep) 1.08s;transform:translate(1840px)}
.gf-footer-motion[data-motion-state=running] .ft-logo .band{animation:7s linear .4s infinite ft-sweep}
@keyframes ft-sweep{0%{transform:translate(0)}43%,to{transform:translate(1840px)}}
@media (prefers-reduced-motion:reduce){.ft-logo .lg{stroke-dashoffset:0;transition:none!important}.ft-logo .rv,.ft-logo .band{transform:translate(1840px);transition:none!important;animation:none!important}}
.ft-logo-m{--draw:cubic-bezier(.45, 0, .2, 1);--sweep:cubic-bezier(.6, 0, .25, 1);aspect-ratio:372/78;background:#04060c;width:100%;height:auto;position:relative;overflow:hidden}
.ft-logo-m svg{width:100%;height:100%;display:block;position:absolute;inset:0}
.ft-logo-m .lg{fill:none;stroke-width:3.441px;stroke-dasharray:1;stroke-dashoffset:1px}
.ft-logo-m .rv,.ft-logo-m .band{transform:translate(0)}
.ft-logo-m.is-in .lg{stroke-dashoffset:0;transition:stroke-dashoffset 1.6s var(--draw) calc(var(--i) * 90ms)}
.ft-logo-m.is-in .rv,.ft-logo-m.is-in .band{transition:transform 1.6s var(--sweep) 1.08s;transform:translate(534.8px)}
.gf-footer-motion[data-motion-state=running] .ft-logo-m .band{animation:7s linear .4s infinite ftm-sweep}
@keyframes ftm-sweep{0%{transform:translate(0)}43%,to{transform:translate(534.8px)}}
@media (prefers-reduced-motion:reduce){.ft-logo-m .lg{stroke-dashoffset:0;transition:none!important}.ft-logo-m .rv,.ft-logo-m .band{transform:translate(534.8px);transition:none!important;animation:none!important}}

/* ---------- Frontal Five: NewsletterForm.astro, compact ---------- */
.gf-newsletter{--nl-focus:var(--gf-blue-300);--nl-focus-ring:rgba(165,204,253,.6);--nl-focus-glow:rgba(165,204,253,.1);--nl-bad:#ff9d9d;--nl-bad-ring:rgba(255,157,157,.7);--nl-ink:#fff;--nl-placeholder:rgba(255,255,255,.5);max-width:100%;position:relative;text-align:left}
.gf-newsletter-form{position:relative;display:flex;align-items:center;gap:12px;width:100%;color:var(--nl-ink);margin:0}
.gf-newsletter[data-state='busy'] .gf-newsletter-form{cursor:progress}
.gf-newsletter[data-state='done'] .gf-newsletter-form{display:none}
.gf-newsletter input{border:0;outline:0;background:transparent;padding:8px 0;min-width:0;min-height:44px;flex:1;font:400 14px/1.4 var(--gf-sans);letter-spacing:-.015em;color:var(--nl-ink);border-radius:0}
.gf-newsletter input::placeholder{color:var(--nl-placeholder)}
.gf-newsletter .gf-newsletter-trap{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden}
.gf-newsletter .gf-button.gf-button[data-gf-theme]{flex:none;white-space:nowrap}
.gf-newsletter[data-state='busy'] .gf-button.gf-button[data-gf-theme]::before{clip-path:inset(0)}
.gf-newsletter-status{margin:0;color:var(--gf-muted,#97989f);font:400 13px/20px var(--gf-sans);letter-spacing:-.015em}
.gf-newsletter-status:empty{display:none}
.gf-newsletter-status:focus{outline:0}
.gf-newsletter[data-state='error'] .gf-newsletter-status{position:absolute;top:calc(100% + 10px);left:0;max-width:100%;color:var(--nl-bad)}
.gf-newsletter[data-state='done'] .gf-newsletter-status{color:#fff;font-size:15px;line-height:1.4}
.gf-newsletter--compact{--nl-corner:rgba(255,255,255,.6);--nl-ring:inset 0 0 0 1px rgba(255,255,255,.2);--nl-ring-focus:inset 0 0 0 1px var(--nl-focus-ring),0 0 0 4px var(--nl-focus-glow);--nl-ring-error:inset 0 0 0 1px var(--nl-bad-ring);width:100%;max-width:360px}
.gf-newsletter--compact .gf-newsletter-form{--nl-bracket:var(--nl-corner);box-sizing:border-box;height:44px;min-height:44px;padding:4px 4px 4px 16px;background:rgba(255,255,255,.1);box-shadow:var(--nl-ring);transition:box-shadow 200ms ease}
.gf-newsletter--compact .gf-newsletter-form::before,.gf-newsletter--compact .gf-newsletter-form::after{content:'';position:absolute;top:-5px;bottom:-5px;width:7px;pointer-events:none;background:linear-gradient(var(--nl-bracket),var(--nl-bracket)) left top/7px 1px no-repeat,linear-gradient(var(--nl-bracket),var(--nl-bracket)) left bottom/7px 1px no-repeat,linear-gradient(var(--nl-bracket),var(--nl-bracket)) left top/1px 7px no-repeat,linear-gradient(var(--nl-bracket),var(--nl-bracket)) left bottom/1px 7px no-repeat;transition:translate 260ms var(--gf-ease-out)}
.gf-newsletter--compact .gf-newsletter-form::before{left:-5px}
.gf-newsletter--compact .gf-newsletter-form::after{right:-5px;transform:scaleX(-1)}
.gf-newsletter--compact .gf-newsletter-form:focus-within{--nl-bracket:var(--nl-focus);box-shadow:var(--nl-ring-focus)}
.gf-newsletter--compact .gf-newsletter-form:focus-within::before{translate:-3px 0}
.gf-newsletter--compact .gf-newsletter-form:focus-within::after{translate:3px 0}
.gf-newsletter--compact[data-state='error'] .gf-newsletter-form{--nl-bracket:var(--nl-bad);box-shadow:var(--nl-ring-error)}
.gf-newsletter--compact input{min-height:44px;height:44px;margin-block:-4px;padding:0}
.gf-newsletter--compact .gf-button.gf-button[data-gf-theme]{--button-inset:0px;min-width:0;min-height:36px;height:36px;padding:0 16px;font:400 13px/1 var(--gf-mono);letter-spacing:.195px}
.gf-newsletter--compact .gf-button.gf-button[data-gf-theme]::after{display:none}
.gf-newsletter--compact[data-state='done'] .gf-newsletter-status{font-size:inherit}
@media(max-width:1024px){.gf-newsletter input{font-size:16px}}
@media(max-width:480px){.gf-newsletter--compact .gf-newsletter-form{gap:6px}}
@media(max-width:700px){.gf-newsletter .gf-button.gf-button[data-gf-theme]{min-height:48px}.gf-newsletter--compact .gf-newsletter-form{height:56px;min-height:56px}.gf-newsletter--compact input{height:56px;min-height:56px}.gf-newsletter--compact .gf-button.gf-button[data-gf-theme]{min-height:48px;height:48px}.gf-newsletter--compact{max-width:none}}
@media(hover:none),(pointer:coarse){.gf-newsletter input{font-size:16px!important}}
@media(prefers-reduced-motion:reduce){.gf-newsletter-form,.gf-newsletter-form::before,.gf-newsletter-form::after,.gf-newsletter input{transition:none}}

/* ============================================================
   The directory's own chrome, built from the tokens above
   ============================================================ */

/* the mount: the site header, then the directory bar, above the page */
.nav-outer{position:relative;z-index:50;background:var(--gf-bg)}

/* The directory bar under the site nav: the directory's name (its home link),
   then its own sections. Hairline below, the shell's width, 14px Inter as the
   site nav sets its links, the current section in the nav's blue. It scrolls
   with the page: only the site nav is fixed, as on the site. */
.gd-subnav{position:relative;z-index:1;border-bottom:1px solid #ffffff1a;font:400 14px/1.4 var(--gf-sans);letter-spacing:-.015em}
.gd-subnav-inner{max-width:1280px;width:calc(100% - var(--gf-gutter)*2);margin-inline:auto;display:flex;align-items:center;gap:32px;min-height:52px}
.gd-subnav a{color:inherit;text-decoration:none}
.gd-subnav :focus-visible{outline:2px solid var(--gf-blue);outline-offset:5px}
.gd-subnav-home{flex:none;display:flex;align-items:center;min-height:44px;font:400 var(--t-11)/1.4 var(--gf-mono);letter-spacing:.04em;text-transform:uppercase;color:rgba(255,255,255,.72)}
.gd-subnav-home[aria-current]{color:var(--gf-blue)}
.gd-subnav-short{display:none}
@media(max-width:760px){.gd-subnav-full{display:none}.gd-subnav-short{display:inline}}
.gd-subnav-links{display:flex;align-items:center;gap:24px;min-width:0}
.gd-subnav-links>a,.gd-drop>summary{display:flex;align-items:center;gap:6px;min-height:44px;color:#fff;white-space:nowrap}
.gd-subnav-links>a[aria-current],.gd-drop.is-current>summary{color:var(--gf-blue)}
@media(hover:hover) and (pointer:fine){.gd-subnav-home:hover,.gd-subnav-links>a:hover,.gd-drop>summary:hover{color:var(--gf-blue)}.gd-subnav-home,.gd-subnav-links>a,.gd-drop>summary{transition:color 200ms var(--gf-ease-hover)}}
.gd-drop{position:relative}
.gd-drop>summary{list-style:none;cursor:pointer}
.gd-drop>summary::-webkit-details-marker{display:none}
.gd-drop>summary::marker{content:''}
.gd-drop-chev{transition:transform 300ms var(--gf-ease-ui)}
.gd-drop[open] .gd-drop-chev{transform:rotate(180deg)}
/* the site's dropdown panel: the ground colour, a #ffffff26 hairline, square, rows
   that tint on hover and the current one in blue */
.gd-drop-menu{position:absolute;top:calc(100% + 4px);left:-14px;z-index:5;width:288px;padding:4px;border:1px solid #ffffff26;background:var(--gf-bg);display:grid;gap:2px}
.gd-drop:not([open])>.gd-drop-menu{display:none}
.gd-drop-menu a{display:flex;align-items:baseline;gap:12px;min-height:44px;padding:11px 14px;font-size:15px;line-height:1.4;color:#fff}
.gd-drop-menu a .n{margin-left:auto;font:400 12px/1.4 var(--gf-mono);color:rgba(255,255,255,.64)}
.gd-drop-menu a[aria-current]{background:#ffffff1a;color:var(--gf-blue)}
.gd-drop-menu a[aria-current] .n{color:var(--gf-blue)}
@media(hover:hover) and (pointer:fine){.gd-drop-menu a:hover{background:#ffffff1a}}
.gd-drop-menu a:focus-visible{outline-offset:-2px;background:#ffffff1a}
@media(prefers-reduced-motion:no-preference){.gd-drop>.gd-drop-menu{transition:opacity 320ms var(--gf-ease-ui),translate 500ms var(--gf-ease-ui)}@starting-style{.gd-drop[open]>.gd-drop-menu{opacity:0;translate:0 -6px}}}
@media(prefers-reduced-motion:reduce){.gd-drop-chev{transition:none}}
/* Small tablets: the bar keeps its row, with the short name, a hairline after it and
   a fading edge, and the dropdown hangs from the whole bar. */
@media(max-width:760px){
  .gd-subnav-inner{width:100%;gap:0;padding-left:16px;min-height:48px}
  .gd-subnav-home{padding-right:16px;margin-right:4px;border-right:1px solid #ffffff1a;font-size:12px}
  .gd-subnav-links{flex:1;gap:20px;overflow-x:auto;scrollbar-width:none;padding:0 32px 0 16px;-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 40px),transparent);mask-image:linear-gradient(90deg,#000 calc(100% - 40px),transparent)}
  .gd-subnav-links::-webkit-scrollbar{display:none}
  .gd-drop{position:static}
  .gd-drop-menu{left:16px;right:16px;width:auto;top:100%}
}
/* Phones, below the width where that row fits (it needs about 530px; the switch sits at
   560px for other platforms' font widths): the bar is one full-width control naming the
   current section, and it opens a panel with every section, each one tap away. A native
   details element, so it works without JavaScript; app.js adds aria-expanded, Escape,
   an outside tap and focus return (initSections). The panel floats over the page, so it
   takes --surface and its hairline, never a translucent fill. */
.gd-sections{display:none}
@media(max-width:559px){
  .gd-subnav-inner{padding-left:0}
  .gd-subnav-home,.gd-subnav-links{display:none}
  .gd-sections{display:block;flex:1;min-width:0}
  .gd-sections-toggle{display:flex;align-items:center;min-height:48px;padding-left:16px;list-style:none;cursor:pointer;color:#fff;-webkit-tap-highlight-color:transparent}
  .gd-sections-toggle::-webkit-details-marker{display:none}
  .gd-sections-toggle::marker{content:''}
  .gd-sections-toggle:focus-visible{outline-offset:-2px}
  .gd-sections-name{flex:none;font:400 12px/1.4 var(--gf-mono);letter-spacing:.04em;text-transform:uppercase;color:rgba(255,255,255,.72)}
  .gd-sections-name.is-current{color:var(--gf-blue)}
  .gd-sections-here{min-width:0;margin-left:14px;padding-left:14px;border-left:1px solid #ffffff1a;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:var(--gf-blue)}
  .gd-sections-icon{flex:none;align-self:stretch;display:flex;align-items:center;justify-content:center;width:48px;margin-left:auto;border-left:1px solid #ffffff1a}
  .gd-sections-icon .gd-drop-chev{width:12px;height:12px}
  .gd-sections[open] .gd-drop-chev{transform:rotate(180deg)}
  .gd-sections-panel{position:absolute;top:100%;left:0;right:0;z-index:5;display:grid;max-height:calc(100dvh - 128px);overflow-y:auto;overscroll-behavior:contain;padding:4px 0 8px;border-top:1px solid #ffffff1a;border-bottom:1px solid #ffffff26;background:var(--surface)}
  .gd-sections:not([open])>.gd-sections-panel{display:none}
  .gd-sections-panel a{display:flex;align-items:center;gap:12px;min-height:48px;padding:0 16px;font-size:15px;line-height:1.4;color:#fff}
  .gd-sections-panel a .n{margin-left:auto;font:400 12px/1.4 var(--gf-mono);color:rgba(255,255,255,.64)}
  .gd-sections-panel a[aria-current]{background:#ffffff1a;color:var(--gf-blue)}
  .gd-sections-panel a[aria-current] .n{color:var(--gf-blue)}
  .gd-sections-panel a:focus-visible{outline-offset:-2px;background:#ffffff1a}
  .gd-sections-label{margin:8px 16px 0;padding-top:12px;border-top:1px solid #ffffff1a;font:400 12px/1.4 var(--gf-mono);letter-spacing:.04em;text-transform:uppercase;color:rgba(255,255,255,.64)}
  .gd-sections-cats{display:grid;margin-top:4px}
  .gd-sections-cats a{padding-left:28px}
  .gd-sections-more{display:grid;margin:8px 16px 0;padding-top:4px;border-top:1px solid #ffffff1a}
  .gd-sections-more a{margin-inline:-16px}
}
@media(max-width:559px) and (hover:hover) and (pointer:fine){.gd-sections-panel a:hover{background:#ffffff1a}}
@media(max-width:559px) and (prefers-reduced-motion:no-preference){.gd-sections>.gd-sections-panel{transition:opacity 320ms var(--gf-ease-ui),translate 500ms var(--gf-ease-ui)}@starting-style{.gd-sections[open]>.gd-sections-panel{opacity:0;translate:0 -6px}}}

/* the directory note above the site footer */
.gd-foot-note{background:var(--gf-bg);border-top:1px solid #ffffff1a}
.gd-foot-note .gf-shell{padding:20px 39px;display:grid;gap:4px}
.gd-foot-note p{margin:0;max-width:96ch;font:400 13px/1.5 var(--gf-sans);letter-spacing:-.015em;color:#97989f}
@media(max-width:700px){.gd-foot-note .gf-shell{padding:20px}}

/* ============================================================
   Launch fixes, 19 Oct 2026 (QA fix list items 12, 13, 14, 19)
   ============================================================ */

/* The booking bar inside the directory's own blocks: its error line takes its own
   row instead of hanging over the line under the bar, and takes no room when empty. */
.gf-booking-error{position:static;margin-top:10px}
.gf-booking-error:empty{display:none}
/* min-width:0, or the wrapper keeps the bar's 402px as its floor: in the 285px row of
   the page CTA on a 390px phone the bar ran 117px past the panel and .cta clipped the
   button to "BOOK" (final QA, 20 Oct). The bar's own max-width:100% then fits it. */
.hero-actions .gd-book{flex:1 1 320px;max-width:402px;min-width:0}
.hero-actions{align-items:center}
/* An input's intrinsic width is about twenty characters, and it counted toward the
   min-content width of every box holding a bar: in the 330px sidebar that pushed the
   whole column out to 411px. The field takes its width from the bar, never the reverse. */
.gf-booking input{width:0}
.prod-side > *{min-width:0}
.facts-book{margin-top:1rem;padding-top:1rem;border-top:1px solid var(--line-soft)}
.facts-book-h{display:block;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--muted);margin-bottom:.7rem}
.facts-book .gf-booking{width:100%}
.facts-book .gf-booking-form{padding-left:12px;gap:8px}
.facts-book .gf-booking .gf-button.gf-button[data-gf-theme]{min-width:132px;--button-pad-x:12px}
.empty .clearall{width:auto;display:inline-block;margin:.6rem 0 0;padding:.4rem .9rem}

/* Item 12. Below 1120px the sidebar dissolves into the page. The factsheet used to be
   ordered before the whole article, so the h1 sat under a screen of facts (551px down
   on a review, 843px on a stack). The article dissolves too now, and the order is:
   breadcrumb row and title, then the facts, then everything else. That is the source
   order now, so nothing here reorders a box (see .prod above). */
@media (max-width:1120px){
  .prod{grid-template-columns:minmax(0,1fr);gap:0}
  .prod-main{display:contents}
  .prod-main > *{min-width:0}
  .prod-side > .facts{margin:.4rem 0 .6rem}
}

/* Item 13. The phone directory: the filters fold behind a toggle beside the result
   count, the search box has a row of its own, and the results start under it. */
.filters-toggle{display:none}
@media (max-width:1000px){
  .dir{display:flex;flex-direction:column;gap:0}
  .dir > .dir-main{display:contents}
  .dir-bar{order:1;flex-direction:column;align-items:stretch;gap:.6rem}
  .dir > .filters{order:2;display:none;margin-bottom:1.2rem}
  .dir.filters-open > .filters{display:block}
  .dir .results{order:3}
  .search{min-width:0;width:100%}
  .dir-meta{display:flex;align-items:center;justify-content:space-between;gap:1rem}
  .filters-toggle{
    display:inline-flex;align-items:center;gap:.5rem;min-height:44px;padding:0 1rem;
    background:rgba(255,255,255,.05);border:1px solid var(--line);color:var(--ink);
    font:400 12px/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;cursor:pointer;
  }
  .filters-toggle .n:not(:empty){display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 5px;background:var(--accent);color:#04060C}
  .filters-toggle[aria-expanded="true"]{border-color:rgba(165,204,253,.5);color:var(--accent)}
}
.dir-meta{display:flex;align-items:center;gap:1rem}
/* 16px in every field a phone can focus, or iOS zooms into it and stays zoomed */
@media (max-width:1024px), (hover:none), (pointer:coarse){
  .search input,.cost-field input,.sortsel{font-size:16px}
}
/* tap targets: 44px on touch screens and narrow windows */
@media (max-width:760px), (hover:none) and (pointer:coarse){
  .fopt{min-height:44px;align-items:center;padding:.3rem 0}
  .fopt input{margin-top:0}
  details.fgroup > summary.flabel{min-height:44px;display:flex;align-items:center}
  .clearall,.facts-more,.toc-more,.cmp-sort,.fd-close{min-height:44px}
  .fd-close{min-width:44px}
  .crumb a{display:inline-flex;align-items:center;min-height:44px}
  .decide-opt{min-height:44px;align-items:center}
  .cl-row{min-height:44px}
  .toc a{min-height:44px;align-items:center}
  .cmp-sort{padding:.5rem .75rem}
  .cost-field input{min-height:44px}
  .search{min-height:44px}
  .tile,.pair,.stack-card,.index-row{min-height:44px}
}
/* a table that scrolls sideways says so above it, and fades at the edge that still has
   columns behind it */
.scroll-hint{display:block;margin:0 0 .5rem;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}
.scroll-hint[hidden]{display:none}
.table-scroll.can-scroll:not(.at-end){-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 56px),rgba(0,0,0,.25));mask-image:linear-gradient(90deg,#000 calc(100% - 56px),rgba(0,0,0,.25))}
.table-scroll.can-scroll:not(.at-start):not(.at-end){-webkit-mask-image:linear-gradient(90deg,rgba(0,0,0,.25),#000 56px,#000 calc(100% - 56px),rgba(0,0,0,.25));mask-image:linear-gradient(90deg,rgba(0,0,0,.25),#000 56px,#000 calc(100% - 56px),rgba(0,0,0,.25))}
.table-scroll.can-scroll.at-end:not(.at-start){-webkit-mask-image:linear-gradient(90deg,rgba(0,0,0,.25),#000 56px);mask-image:linear-gradient(90deg,rgba(0,0,0,.25),#000 56px)}

/* Item 14. Grids that left one cell alone. Six stage tiles take three columns (two
   rows of three) rather than four and two; four plans take two columns rather than
   three and one. */
.tiles:has(> .tile:nth-child(6):last-child),.tiles:has(> .tile:nth-child(6):nth-last-child(2)){grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr))}
@media (min-width:1001px){
  .tiles:has(> .tile:nth-child(6):last-child),.tiles:has(> .tile:nth-child(6):nth-last-child(2)){grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (min-width:561px){
  .tiers:has(> :nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}
}

/* the copy button on a "Drop it into Claude Code" snippet */
.callout{position:relative}
.snippet-copy{
  position:absolute;top:.7rem;right:.7rem;min-height:32px;padding:0 .7rem;
  background:rgba(255,255,255,.05);border:1px solid var(--line);color:var(--ink);
  font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;cursor:pointer;
}
.snippet-copy:hover{border-color:rgba(165,204,253,.5);color:var(--accent)}
.callout:has(> .snippet-copy) .lbl{padding-right:5.5rem}
@media (max-width:760px), (hover:none) and (pointer:coarse){.snippet-copy{min-height:44px}}

/* Focus, as the site draws it: a 2px blue square outline 5px out */
:focus-visible{outline:2px solid var(--gf-blue);outline-offset:5px}
input:focus-visible,select:focus-visible{outline-offset:2px}
::selection{color:#04060c;background:var(--gf-blue)}

/* Reduced motion, as grafit.css sets it: nothing animates, and nothing scrolls smoothly */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{scroll-behavior:auto!important;animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
}
/* the busy label ("Opening calendar") at the phone minimum too: the site sets it at
   11px under 421px, so the button grows by the few pixels 12px needs */
@media (max-width:420px){
  .gf-booking-busy{font-size:12px}
  .gf-booking .gf-button.gf-button[data-gf-theme]{min-width:136px}
}
/* JetBrains Mono ships one weight: a price set in .mono stays at 400 inside bold prose,
   and a <b> inside .mono marks itself with the ink colour rather than a fake bold */
.mono{font-weight:400}
.mono b,.mono strong{font-weight:400;color:var(--ink)}
/* the browser's "smaller" took a unit after a mono price to 10.8px on a phone */
small{font-size:max(12px,.85em)}

/* ============================================================
   Final QA, 20 Oct 2026: shared components matched to the site
   ============================================================
   Measured on the launch at 858fbf9 (the plays library and a play page, the home FAQ,
   the service pages), and re-matched at f779d4b to the site's consistency pass (ffcdf5e):
   the chip, the text field, the reading colour, the bracket eyebrow and the footer's mail
   glyph. The directory keeps its own layout; these are the pieces it shares with the
   site, so they now read the same. */

/* Nothing past the edge at 320px. The search field's input counted its intrinsic twenty
   characters toward the bar's min-content, which pushed .dir-bar to 330px on a 320px
   phone; a long MCP auth chip did the same to its card. */
.dir-bar{min-width:0}
/* .dir keeps align-items:start from its desktop grid, so in the phone column the search
   bar and the open filter panel shrank to their content, two thirds of the row or less */
@media (max-width:1000px){.dir > .filters,.dir .dir-bar,.dir .results{align-self:stretch}}
/* The compare header's two sides were bare 1fr tracks, which never shrink below their
   min-content, so a long tagline or a nowrap price pill held a side wider than its share:
   at 390 Dripify vs Expandi lost Expandi's tagline and price, and at 320 sixteen compare
   headers were cut by 3 to 95px (final QA, 20 Oct). The sides share the row and their
   words wrap. */
.vs-head{grid-template-columns:minmax(0,1fr) auto minmax(0,1fr)}
.vs-side{min-width:0}
.vs-side h2,.vs-side .tag{max-width:100%;overflow-wrap:break-word}
/* a 320px phone gives each side about 106px: at 18px "RocketReach" and "Woodpecker" need more
   and would break mid-word, at 16px every vendor name in the directory fits whole */
@media (max-width:380px){.vs-side h2{font-size:16px}}
/* the price wraps at the slashes gen-compare.js marks with <wbr>, so the pill is an inline
   block (in a flex box each <wbr> would split the text into items); anywhere is the last
   resort for a price with no slash */
.vs-side .pill{display:inline-block;max-width:100%;white-space:normal;overflow-wrap:anywhere;text-align:center}
/* A tile title next to its count, and a card title next to its flag, give way rather than
   push: "People Data Labs alternatives" held its tool chip on one line and ran the count 2px
   past a 320px screen, and "Read only" left "ZeroBounce MCP" 78px. The title wraps, and a
   flag that no longer fits beside the title drops under it. */
.tile-head h3{min-width:0}
.tile-head .tool{white-space:normal}
.card-top{flex-wrap:wrap}
.card-title{flex:1 1 9rem}
.search input{width:0}
.card-tags{grid-template-columns:minmax(0,1fr)}
.tag-row .pill{max-width:100%;white-space:normal}

/* Square mono buttons and chips: the site's one chip since its consistency pass (ffcdf5e:
   the plays .gp-chips button and .gp-reset, the blog filters), 32px tall with a mouse and
   44px on a touch screen, 11px mono at .04em, 6px by 14px, a hairline, a faint blue wash on
   hover, and the blue fill when pressed. Clear all filters, the differences sort and the
   phone Filters toggle are that chip; See all and Show more keep their sidebar size so the
   trim levels do not move, and only take the type and the square corner. */
.clearall,.cmp-sort,.filters-toggle,.empty .clearall{
  min-height:32px;padding:6px 14px;border:1px solid var(--line-cell);border-radius:0;background:transparent;
  color:rgba(255,255,255,.64);font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;
  transition:background-color 200ms var(--gf-ease-hover),color 200ms var(--gf-ease-hover),border-color 200ms var(--gf-ease-hover)
}
.clearall{width:100%;margin-top:1rem}
.empty .clearall{width:auto;display:inline-block;margin:.6rem 0 0}
.cmp-sort{display:block;margin-top:.5rem}
.cmp-sort[aria-pressed="true"],.filters-toggle[aria-expanded="true"]{background:var(--accent);border-color:var(--accent);color:#04060C}
.filters-toggle[aria-expanded="true"] .n:not(:empty){background:#04060C;color:var(--accent)}
@media(hover:hover) and (pointer:fine){
  .clearall:hover,.cmp-sort[aria-pressed="false"]:hover,.cmp-sort:not([aria-pressed]):hover,.filters-toggle[aria-expanded="false"]:hover{background:rgba(165,204,253,.1);border-color:var(--line-cell);color:#fff}
}
.facts-more,.toc-more{border-radius:0;font:400 var(--t-11)/1.4 var(--mono);letter-spacing:.04em;text-transform:uppercase;color:rgba(255,255,255,.64)}
.fd-close{border-radius:0}
@media(hover:none),(pointer:coarse){.clearall,.cmp-sort,.filters-toggle,.empty .clearall{min-height:44px}}
@media(max-width:700px){.clearall,.cmp-sort,.filters-toggle,.empty .clearall{padding:6px 12px}}
/* The sort select and the search field are the site's one text field since ffcdf5e (the
   /newsletter field, which the plays and blog searches took): 48px, square, a 38% inset
   hairline on a 6% fill, 15px text, the placeholder at 42%, and on focus a 2px blue inset
   with a 4px ring. */
.search{border:0;border-radius:0;background:rgba(255,255,255,.06);box-shadow:inset 0 0 0 1px rgba(255,255,255,.38);min-height:48px;padding:0 16px}
.search:focus-within{border:0;box-shadow:inset 0 0 0 2px var(--accent),0 0 0 4px rgba(165,204,253,.1)}
.search input{font-size:15px;letter-spacing:-.015em}
.search input::placeholder{color:rgba(255,255,255,.42);opacity:1}
.sortsel{
  appearance:none;-webkit-appearance:none;box-sizing:border-box;height:48px;min-height:48px;padding:0 40px 0 16px;border:0;border-radius:0;outline:0;
  background:rgba(255,255,255,.06) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23a5ccfd' stroke-width='1.4'/%3E%3C/svg%3E") no-repeat right 16px center;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.38);
  font:400 15px/1.4 var(--sans);letter-spacing:-.015em;color:#fff
}
.sortsel:focus{box-shadow:inset 0 0 0 2px var(--accent),0 0 0 4px rgba(165,204,253,.1)}
.sortsel option{background:#0d0f15;color:#fff}
@media (max-width:1024px), (hover:none), (pointer:coarse){.search input,.sortsel{font-size:16px}}

/* The FAQ row of the home page (FAQ.astro): 23px by 24px, the plus in its own 10px slot
   20px after the question, the answer at 64% white, sliding 16px down as it opens. The
   horizontal stroke is now an item at the end of the row; the vertical one still turns. */
.faq{background:radial-gradient(ellipse at 35% 40%,#24304622,transparent 70%)}
.faq summary{display:flex;align-items:center;justify-content:space-between;gap:20px;padding:23px 24px}
/* both strokes are items at the end of the row, the second pulled back over the first
   (the 20px gap plus its own 10px), so they share one centre line and the minus stays 1px */
/* The site sets opacity .6 once, on a span holding both strokes; here each stroke is its own
   box, so .6 on each doubled up where the minus overlaps. A solid colour, white at .6 over
   the open row's ground, reads the same and stays the same where they overlap. */
.faq summary::before,.faq summary::after{position:static;flex:none;background:#9fa0a2;opacity:1}
.faq summary::before{order:1;margin-left:auto}
.faq summary::after{order:2;margin-left:-30px}
.faq .ans p{color:rgba(255,255,255,.64)}
.faq details:not([open])>.ans{display:none}
@media(prefers-reduced-motion:no-preference){
  .faq details[open]>.ans{transition:opacity 300ms ease-out,transform 300ms ease-out}
  @starting-style{.faq details[open]>.ans{opacity:0;transform:translateY(-16px)}}
}
@media (max-width:700px){.faq summary{padding:21px 16px}}

/* The play page breadcrumb (.gp-crumbs): 12px mono at .04em, the links blue, the current
   page at 64%, the slashes at 30%. */
.crumb{font-size:12px;line-height:1.4;letter-spacing:.04em;color:rgba(255,255,255,.64);gap:6px 10px}
.crumb a{color:var(--accent)}
.crumb a:hover{color:var(--accent)}
@media(hover:hover) and (pointer:fine){.crumb a:hover{text-decoration:underline;text-underline-offset:4px}}
.crumb > span{color:rgba(255,255,255,.3)}
.crumb > span:last-child{color:rgba(255,255,255,.64)}

/* A link in running text is the site's .gp-link: blue, a 40% underline 3px under it, the
   full colour on hover. Cards, buttons, tiles and the referral logos carry a class and
   keep their own look. */
main :is(p,dd,.ans,.prose li) a:not([class]){
  color:var(--accent);text-decoration:underline;text-decoration-color:rgba(165,204,253,.4);text-decoration-thickness:1px;text-underline-offset:3px;
  transition:text-decoration-color 200ms var(--gf-ease-hover)
}
@media(hover:hover) and (pointer:fine){main :is(p,dd,.ans,.prose li) a:not([class]):hover{text-decoration-color:currentColor}}
@media(prefers-reduced-motion:reduce){main :is(p,dd,.ans,.prose li) a:not([class]){transition:none}}

/* Long-form reading text at the site's one reading style (--gp-body and --gf-cs-body, 76%
   white since ffcdf5e). Ledes, captions and cards keep 64% and below, as the site's do. */
:root{--ink-read:rgba(255,255,255,.76)}
.prose p,.prose li,.prod-main > section > p:not(.note):not(.tiny),.prod-main > p,.guide-item p,
.callout p,.ugc-read,.vid-read,.decide-out p,.rules dd,.panel li,
.band > .wrap > p:not([class]),.band--paper > .wrap > p:not([class]){color:var(--ink-read)}

/* On a phone nothing a reader reads sits under 15px (the site's phone floor for running
   text: .gp-card__facts, .gp-empty, .gp-dt__items). Labels and chips stay mono. */
@media (max-width:700px){
  .note:not(.mono),.tiny:not(.mono),.card-bullets li,.tile p,.pair p,.stack-card p,.index-row p,.step p,.panel li,.tier li,
  .cta .cta-promise,.reviewer-role,.fact dd,.fopt,td,.empty,.vnote{font-size:15px}
  .card-foot .tiny:not(.mono){font-size:15px}
}

/* Primary and secondary buttons at the site's 48px on a phone, as its booking bar and
   newsletter buttons are. */
@media (max-width:700px){.gd-btn.gf-button.gf-button[data-gf-theme]{min-height:48px}}

/* ============================================================
   Tool marks, 20 Oct 2026: frontal.so's logo guidelines
   ============================================================
   Hard rule 11 of the website repo (docs/LOGO-GUIDELINES.md there): every vendor shows its
   own vector symbol in one colour set by CSS, in a square hairline tile, or a JetBrains Mono
   monogram where no official vector exists. This is the site's src/styles/tool-marks.css at
   ef65e0a, drawn by toolMark() in data.js: the mark is its SVG used as a mask over
   currentColor, white at 72% on dark and 100% on hover or focus. The site's tile is 28px with
   the mark in a 16px box; a placement that needs another size sets --tm-k and the whole tile
   scales with it, so the treatment is the same everywhere. The CTA card visual is a graphic
   and uses the brand tone, the mark in its brand colour on a white tile, as the site's
   service screens do; nothing else does. */
.tm{
  --tm-k:1;
  display:inline-grid;flex:none;place-items:center;box-sizing:border-box;
  width:calc(28px * var(--tm-k));height:calc(28px * var(--tm-k));
  border:1px solid rgba(255,255,255,.12);border-radius:0;background:rgba(255,255,255,.04);
  color:rgba(255,255,255,.72);text-decoration:none;vertical-align:middle;
  transition:color 200ms ease
}
.tm:is(:hover,:focus-visible),:is(a,button):is(:hover,:focus-visible) > .tm{color:#fff}
.tm__glyph{
  display:block;width:calc(var(--tm-w,16px) * var(--tm-k));height:calc(var(--tm-h,16px) * var(--tm-k));
  background-color:currentColor;
  -webkit-mask:var(--tm-file) center/contain no-repeat;mask:var(--tm-file) center/contain no-repeat
}
/* the monogram: the site sets it at 11px in the 28px tile; it scales with the tile, never under 9px */
.tm__letters{font:400 max(9px, calc(11px * var(--tm-k)))/1 var(--mono);letter-spacing:0;text-transform:uppercase}
.tm--brand{color:#04060C;background:#FFFFFF;border-color:rgba(255,255,255,.12)}
.tm--brand .tm__glyph{background-color:var(--tm-brand,#04060C)}
@media (prefers-reduced-motion:reduce){.tm{transition:none}}
/* forced colours drop background colours, so the mask is filled with the text colour */
@media (forced-colors:active){.tm__glyph{forced-color-adjust:none;background-color:CanvasText}}
/* placements: the tile replaces the boxed <img> each of these used to draw */
h3 > .tm,h3 > a.aff{margin-right:.5rem;vertical-align:-5px}

/* ============================================================
   Buttons in rows, 20 Oct 2026 (Alex: "the button in the tool directory are a bit weird")
   ============================================================
   The button itself is the site's .gf-button, untouched. This is only how they sit together.
   A .gf-button paints its fill (and a secondary its brackets) 4px inside its 44px target, the
   --button-inset, so a row is pulled out by those 4px: the fills, not the targets, land on
   the content edges, flush with the text, the hairline above and the fact rows below. A
   primary's brackets sit outside its fill and hang 4.5px past the edge, as the booking bar's do.
   - .gd-btns: a row that spans its box in equal columns (the card foot, the dialog foot on a
     phone). Two columns while each fill can hold a 15-letter label ("Read the review",
     120px) with 12px either side, so a 152px target; one column below that (a 304px foot,
     so phones up to about 400px). The 8px gap leaves 16px between two fills, the site's gap
     between its case-study service buttons.
   - .gd-btns--fit: equal columns as wide as the longest label (the dialog foot).
   - .hero-actions holding buttons: the same pull to the left edge and, as the site's own hero
     pairs (/resources), natural widths with a 14px gap; on a phone one full-width button
     per row. Beside a booking bar only the phone rule applies: the secondary under the bar
     takes the bar's width (no wider than its 402px). */
.gd-btns{
  display:grid;gap:8px;margin-inline:-4px;
  grid-template-columns:repeat(auto-fit,minmax(max(152px,calc((100% - 8px)/2)),1fr))
}
.gd-btns > .gf-button.gf-button[data-gf-theme]{width:100%;--button-pad-x:12px}
.gd-btns--fit{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);width:max-content;max-width:calc(100% + 8px)}
.gd-btns--fit > .gf-button.gf-button[data-gf-theme]{--button-pad-x:24px}
@media (max-width:600px){
  .fd-foot .gd-btns--fit{flex:none;width:calc(100% + 8px);max-width:none;grid-auto-flow:row;grid-template-columns:repeat(auto-fit,minmax(max(152px,calc((100% - 8px)/2)),1fr))}
  .fd-foot .gd-btns--fit > .gf-button.gf-button[data-gf-theme]{--button-pad-x:12px}
}
.hero-actions:has(> .gd-btn):not(:has(> .gd-book)){margin-left:-4px;gap:14px}
@media (max-width:700px){
  .hero-actions:has(> .gd-btn):not(:has(> .gd-book)){display:grid;grid-template-columns:minmax(0,1fr);gap:8px;margin-right:-4px}
  .hero-actions:not(:has(> .gd-book)) > .gd-btn{width:100%}
  .hero-actions:has(> .gd-book) > .gd-btn{width:calc(100% + 8px);max-width:410px;margin-inline:-4px}
  /* a full-width label is centred, so the padding is only its floor: the card rows' 12px (the
     site's booking button below 420px) keeps "Read the People Data Labs review" on one line at 320 */
  .hero-actions > .gd-btn.gf-button.gf-button[data-gf-theme]{--button-pad-x:12px}
}
