/* Creative Products Enterprises — site.css
   The committed world is David Te's Concept 8. See DESIGN.md.
   Every colour pairing below carries its measured WCAG ratio. Do not substitute
   a value without recomputing it. */

/* ── fonts ─────────────────────────────────────────────────────────────── */
/* One file, not three. intertight-500/600/700.woff2 were byte-identical: the same
   variable font shipped under three names, so a cold visit fetched 135 KB to render
   45 KB of typeface and no cache hit could ever apply. The browser instances the
   weight axis from the one file. */
@font-face{font-family:'Inter Tight';src:url('/assets/fonts/intertight.woff2') format('woff2');
  font-weight:500 700;font-style:normal;font-display:swap}

/* ── tokens ────────────────────────────────────────────────────────────── */
:root{
  /* ground */
  --ink:#1B0B10;            /* on --paper 18.0:1 */
  --ground:#2A0B14;         /* dark bands */
  --ground-lift:#3A1220;    /* raised surface on dark */
  --ground-deep:#1E070E;    /* footer, deepest */
  --paper:#FFF7F2;
  --paper-tint:#F6E8E1;

  /* brand — guide values, not the spot-colour screen rendering */
  --orange:#EC672C;         /* PMS 165 C. On --ground 5.67:1 ✓ body. On --paper 3.05:1 — large only */
  --orange-ink:#A64512;     /* orange darkened for text. On --paper 5.70:1, on --paper-tint 5.04:1 ✓
                               (the tint ground is what the first value missed) */
  --magenta:#C8017D;        /* PMS 233 C. On --paper 5.33:1 ✓. On --ground 3.25:1 — large only */
  --orange-soft:rgba(236,103,44,.12);

  /* text */
  --on-dark:#FFF3EC;        /* on --ground 16.68:1 */
  --on-dark-2:#D8B9AC;      /* secondary on --ground 9.91:1 — tinted from the hue, never grey */
  --ink-2:#6B4A50;          /* secondary on --paper 7.29:1 */

  --hair:rgba(27,11,16,.14);          /* decorative dividers — 1.4.11 does not apply */
  --hair-strong:rgba(27,11,16,.28);   /* decorative, heavier */
  /* Where a border IS the control — input, ghost button, burger — it has to clear
     3:1 on its own ground. .28 measured 1.89:1; .45 measures 3.04:1 on --paper. */
  --edge:rgba(27,11,16,.45);
  --edge-dark:rgba(255,243,236,.36);  /* on --ground 3.11:1 ✓ */
  --hair-dark:rgba(255,243,236,.18);
  --hair-dark-strong:rgba(255,243,236,.34);

  --shadow-1:0 1px 2px rgba(27,11,16,.06), 0 4px 12px -4px rgba(27,11,16,.10);
  --shadow-2:0 2px 4px rgba(27,11,16,.07), 0 14px 32px -12px rgba(27,11,16,.20);
  --shadow-3:0 4px 8px rgba(27,11,16,.09), 0 28px 60px -20px rgba(27,11,16,.30);

  --wrap:1240px;
  --gut:clamp(20px,5vw,64px);
  --band:clamp(4.5rem,9vw,8rem);
  --r:14px;
  --r-lg:22px;

  --ease:cubic-bezier(.16,1,.3,1);
  --ease-out:cubic-bezier(.22,.61,.36,1);
}

/* ── reset ─────────────────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}
body{margin:0;background:var(--paper);color:var(--ink);
  font-family:'Inter Tight',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  font-weight:500;font-size:1.0625rem;line-height:1.65;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  overflow-x:hidden}
img,svg,video{max-width:100%;height:auto;display:block}
h1,h2,h3,h4{margin:0;font-weight:700;text-wrap:balance;letter-spacing:-.03em;line-height:1.06}
p{margin:0}
a{color:inherit}
ul,ol{margin:0;padding:0;list-style:none}
button,input,select,textarea{font:inherit;color:inherit}
/* The ring changes colour with the ground rather than staying one orange:
   --orange measures 3.03:1 on --paper and only 2.68:1 on --paper-tint, so it
   fails the 3:1 minimum for a control boundary on every tinted band. */
:focus-visible{outline:2.5px solid var(--orange-ink);outline-offset:3px;border-radius:4px}
                                      /* on --paper 5.70:1, on --paper-tint 5.04:1 ✓ */
.dark :focus-visible,.deep :focus-visible,.phead :focus-visible{outline-color:var(--orange)}
                                      /* on --ground 5.66:1 ✓ */
/* White on this orange is 3.21:1 and fails, which is why the button label is ink.
   Selected text was still doing it. */
::selection{background:var(--orange);color:var(--ink)}   /* 5.94:1 ✓ */

/* ── type scale ────────────────────────────────────────────────────────── */
.d1{font-size:clamp(2.6rem,6.2vw,5.25rem);letter-spacing:-.035em;line-height:.98}
.d2{font-size:clamp(1.95rem,3.8vw,3.1rem);letter-spacing:-.03em;line-height:1.05}
.d3{font-size:clamp(1.45rem,2.6vw,2.05rem);letter-spacing:-.025em;line-height:1.12}
.h3{font-size:clamp(1.2rem,1.9vw,1.55rem);font-weight:600;letter-spacing:-.015em;line-height:1.2}
.lede{font-size:clamp(1.12rem,1.5vw,1.35rem);line-height:1.5;letter-spacing:-.005em}
.small{font-size:.9375rem;line-height:1.55}
.tiny{font-size:.8125rem;line-height:1.5;letter-spacing:.01em}
.measure{max-width:68ch}
.measure-s{max-width:56ch}

/* ── layout ────────────────────────────────────────────────────────────── */
.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}
.band{padding-block:var(--band)}
.band-tight{padding-block:clamp(3rem,6vw,5rem)}
.dark{background:var(--ground);color:var(--on-dark)}
.dark h1,.dark h2,.dark h3{color:var(--on-dark)}
.tint{background:var(--paper-tint)}
.deep{background:var(--ground-deep);color:var(--on-dark)}
.stack>*+*{margin-top:var(--s,1rem)}
.rule{height:1px;background:var(--hair);border:0;margin:0}
.dark .rule{background:var(--hair-dark)}

/* section head: more space above a heading than below it */
.head{margin-bottom:clamp(2rem,4vw,3.25rem)}
.head .d2+p{margin-top:1.1rem;color:var(--ink-2)}
.dark .head .d2+p{color:var(--on-dark-2)}

/* ── skip link ─────────────────────────────────────────────────────────── */
.skip{position:absolute;left:-9999px;top:0;z-index:200;background:var(--ground);
  color:var(--on-dark);padding:.9rem 1.3rem;border-radius:0 0 var(--r) 0;font-weight:600}
.skip:focus{left:0}

/* ── masthead ──────────────────────────────────────────────────────────── */
.mast{position:sticky;top:0;z-index:100;background:rgba(255,247,242,.88);
  backdrop-filter:saturate(150%) blur(14px);-webkit-backdrop-filter:saturate(150%) blur(14px);
  border-bottom:1px solid var(--hair)}
.mast-in{display:flex;align-items:center;gap:1.5rem;min-height:74px}
.brand{display:flex;align-items:center;flex:0 0 auto;padding:.4rem 0}
.brand img{width:auto;height:38px}
@media (max-width:600px){.brand img{height:32px}}
.nav{margin-left:auto;display:flex;align-items:center;gap:.15rem}
.nav a{position:relative;text-decoration:none;font-weight:600;font-size:.9375rem;
  letter-spacing:-.01em;padding:.6rem .78rem;border-radius:9px;color:var(--ink);
  transition:background .18s var(--ease-out),color .18s var(--ease-out)}
.nav a:hover{background:var(--orange-soft);color:var(--orange-ink)}
.nav a[aria-current="page"]{color:var(--orange-ink)}
.nav a[aria-current="page"]::after{content:"";position:absolute;left:.78rem;right:.78rem;
  bottom:.28rem;height:2px;background:var(--orange);border-radius:2px}
.nav-cta{margin-left:.6rem}
.burger{display:none;margin-left:auto;width:46px;height:46px;border:1px solid var(--edge);
  background:transparent;border-radius:11px;align-items:center;justify-content:center;cursor:pointer}
.burger svg{width:20px;height:20px;stroke:var(--ink);stroke-width:1.6;fill:none;
  stroke-linecap:round}
@media (max-width:1000px){
  .nav{position:fixed;inset:74px 0 auto 0;flex-direction:column;align-items:stretch;gap:0;
    background:var(--paper);border-bottom:1px solid var(--hair);padding:.6rem var(--gut) 1.4rem;
    transform:translateY(-8px);opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .22s var(--ease-out),transform .28s var(--ease),visibility .22s;
    max-height:calc(100dvh - 74px);overflow-y:auto;box-shadow:var(--shadow-2)}
  .nav[data-open="true"]{transform:none;opacity:1;visibility:visible;pointer-events:auto}
  .nav a{padding:.95rem .2rem;font-size:1.0625rem;border-radius:0;
    border-bottom:1px solid var(--hair)}
  .nav a[aria-current="page"]::after{display:none}
  .nav-cta{margin:1rem 0 0}
  .nav-cta .btn{width:100%;justify-content:center}
  .burger{display:inline-flex}
}

/* ── buttons ───────────────────────────────────────────────────────────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.55rem;
  min-height:50px;padding:.85rem 1.5rem;border-radius:11px;text-decoration:none;
  font-weight:600;font-size:.9688rem;letter-spacing:-.005em;border:1.5px solid transparent;
  cursor:pointer;transition:transform .16s var(--ease-out),background .18s,
  border-color .18s,box-shadow .18s;white-space:nowrap}
/* nowrap keeps short labels on one line, which is what it is for. But a descriptive
   label — "What we do for promotional distributors and resellers" — then measures
   460px and overflows a phone. Let long labels wrap once the screen is narrow. */
@media (max-width:700px){.btn{white-space:normal;text-align:center}}
.btn svg{width:17px;height:17px;flex:0 0 auto}
/* #EC672C is pinned by the brand guide, so the label moves rather than the fill:
   white on this orange is 3.21:1 and fails AA, ink on it is 5.94:1 and passes. */
.btn-primary{background:var(--orange);color:var(--ink);box-shadow:var(--shadow-1)}
.btn-primary:hover{background:#F5762F;box-shadow:var(--shadow-2);transform:translateY(-1px)}
.btn-primary:active{transform:translateY(0);box-shadow:var(--shadow-1)}
.btn-ghost{border-color:var(--edge);color:var(--ink);background:transparent}
.btn-ghost:hover{border-color:var(--ink);background:rgba(27,11,16,.04)}
.dark .btn-ghost{border-color:var(--edge-dark);color:var(--on-dark)}
.dark .btn-ghost:hover{border-color:var(--on-dark);background:rgba(255,243,236,.08)}
.btns{display:flex;flex-wrap:wrap;gap:.75rem}
.btn:disabled,.btn[aria-disabled="true"]{opacity:.5;cursor:not-allowed;transform:none}

/* text link with a real underline offset */
.link{color:var(--orange-ink);text-decoration:underline;text-underline-offset:3px;
  text-decoration-thickness:1.5px;font-weight:600;transition:color .16s}
.link:hover{color:var(--ink)}
.dark .link{color:var(--orange)}
.dark .link:hover{color:var(--on-dark)}

/* ── hero ──────────────────────────────────────────────────────────────── */
.hero{position:relative;background:var(--ground);color:var(--on-dark);overflow:hidden;
  padding-block:clamp(3.5rem,7vw,6rem) clamp(4rem,8vw,7rem)}
.hero::after{content:"";position:absolute;inset:auto 0 0 0;height:1px;background:var(--hair-dark)}
.hero-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(2rem,5vw,4.5rem);
  align-items:center}
@media (max-width:900px){.hero-grid{grid-template-columns:1fr;gap:2.5rem}}
.hero h1{color:var(--on-dark)}
.hero .lede{color:var(--on-dark-2);margin-top:1.5rem;max-width:46ch}
.hero .btns{margin-top:2.25rem}
.hero-stats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1.25rem 2rem;
  margin-top:clamp(2.25rem,4vw,3rem);padding-top:1.75rem;border-top:1px solid var(--hair-dark)}
@media (max-width:1100px){.hero-stats{grid-template-columns:1fr 1fr}}
.hero-stat{min-width:0}
.hero-stat b{display:block;font-size:1.0625rem;font-weight:700;color:var(--orange);
  letter-spacing:-.01em}
.hero-stat span{display:block;font-size:.875rem;color:var(--on-dark-2);margin-top:.1rem}

/* the mark — the site's one authored motion moment */
.mark-stage{position:relative;display:grid;place-items:center;min-height:min(58vw,420px)}
.cpe-mark{width:min(100%,420px);height:auto;overflow:visible}
/* Each .mk path carries its own matrix() from the artwork, resolved in the SVG's
   user space. transform-box/transform-origin re-anchor that matrix to the shape's
   own bounding box, which flips shapes about the wrong point and mangles the mark.
   Leave the default view-box origin alone. mark.js composes translate() in front. */
.cpe-mark .mk{will-change:transform}
/* David, 24 Sep: the stacked lockup assembles where the mark opened, so the stage never
   ends empty. Sam, same day: larger, and landing a little higher than the mark sat.
   `top` rather than a transform, because mark.js owns the transform. Hidden until
   mark.js drives it, so without JS the mark stays whole. */
.mark-stage>*{grid-area:1/1}
.hero-lockup{position:relative;top:-3rem;width:min(86%,380px);height:auto;overflow:visible;
  opacity:0;pointer-events:none;transform-origin:50% 40%;will-change:transform}
/* On a phone the proposition comes first. Leading with the mark meant a buyer saw a
   logo for a whole screen before a single word about what the company does. Under the
   copy the reveal is centred (Sam, 24 Sep), and the lockup lands a little wider than
   the mark it replaces, about two thirds of a phone's width. */
@media (max-width:900px){.mark-stage{min-height:0;order:0;margin-top:.5rem}
  .cpe-mark{width:min(64%,300px)}.hero-lockup{top:0;width:min(70%,330px)}}

/* ── business cards — deliberately not four identical tiles ────────────── */
.biz{display:grid;gap:1.25rem;grid-template-columns:repeat(6,1fr)}
.biz-card{grid-column:span 3;position:relative;display:flex;flex-direction:column;
  background:var(--paper);border:1px solid var(--hair);border-radius:var(--r-lg);
  text-decoration:none;overflow:hidden;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .3s}
.biz-body{display:flex;flex-direction:column;flex:1;
  padding:clamp(1.5rem,2.4vw,2.1rem)}
.biz-card:nth-child(1){grid-column:span 4}
.biz-card:nth-child(2){grid-column:span 2}
.biz-card:nth-child(3){grid-column:span 2}
.biz-card:nth-child(4){grid-column:span 4}
.biz-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-3);border-color:transparent}
/* No coloured stripe on the card — it is the most recognisable AI-UI tell and the
   craft floor bans it. The accent earns its place under the logo instead, where it
   reads as part of that brand rather than as decoration on a container. */
.biz-card .biz-logo{padding-bottom:1.05rem;border-bottom:2px solid var(--acc,var(--orange))}
/* One box for all four group marks. Per-brand pixel heights made the most compact
   mark render narrowest and read as demoted — David, 27 Aug: 'all logos should have
   equal importance'. Each mark now fits the same box and fills what it can. */
.biz-logo{width:auto;max-width:min(74%,150px);max-height:56px;height:auto;
  object-fit:contain;object-position:left center;
  margin-bottom:1.05rem;box-sizing:content-box}
.biz-role{font-size:.8125rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--ink-2)}
.biz-card p{margin-top:.7rem;color:var(--ink-2);max-width:44ch}
.biz-go{margin-top:auto;padding-top:1.4rem;display:inline-flex;align-items:center;gap:.45rem;
  font-weight:600;font-size:.9375rem;color:var(--orange-ink)}
.biz-go svg{width:16px;height:16px;transition:transform .25s var(--ease)}
.biz-card:hover .biz-go svg{transform:translateX(4px)}
.biz-card .biz-shot{width:100%;flex:1 1 auto;min-height:210px;object-fit:cover;display:block}
.biz-body{flex:0 0 auto}
@media (max-width:860px){.biz-card .biz-shot{flex:0 0 auto;aspect-ratio:16/9;min-height:0}}
@media (max-width:860px){
  .biz-card,.biz-card:nth-child(n){grid-column:span 6}
}

/* ── prose rhythm ──────────────────────────────────────────────────────── */
/* Tight between paragraphs, generous after the opener, and more space above a
   heading than below it. Flat 1rem everywhere was the tell the detector caught. */
.prose{color:var(--ink-2);max-width:66ch}
.prose>p{margin-top:.85rem}
.prose>p:first-child{margin-top:0}
.prose>p:first-of-type{font-size:1.09rem;line-height:1.6;color:var(--ink)}
.prose>p:first-of-type+p{margin-top:1.35rem}
.prose>h2,.prose>h3{margin-top:2.6rem}
.prose>h2+p,.prose>h3+p{margin-top:.75rem}
.prose>.ticks{margin-top:1.75rem}
.lede-2{margin-top:1.2rem;color:var(--ink-2);max-width:66ch}
.p-2{margin-top:.9rem;color:var(--ink-2);max-width:66ch}
.dark .lede-2,.dark .p-2{color:var(--on-dark-2)}
.cap-line{margin-top:.6rem;font-weight:600;color:var(--orange-ink)}
/* .prose>p:first-child zeroes its own top margin, and .cap-line sits outside
   .prose, so the two collided at 0px against 22px elsewhere in the same block. */
.cap-line + .prose{margin-top:1.35rem}
.dark .cap-line{color:var(--orange)}
.dark .prose{color:var(--on-dark-2)}
.dark .prose>p:first-of-type{color:var(--on-dark)}

/* ── market / capability rows ──────────────────────────────────────────── */
.rows{display:grid;gap:clamp(2.5rem,5vw,4.5rem)}
.row{display:grid;grid-template-columns:1fr 1fr;gap:clamp(1.75rem,4vw,3.5rem);align-items:start}
/* Grid items default to min-width:auto, so one unbreakable child — a nowrap button
   with a long label — can push the whole track wider than the viewport. */
.row>*{min-width:0}
.row:nth-child(even) .row-media{order:-1}
@media (max-width:860px){.row{grid-template-columns:1fr}.row:nth-child(even) .row-media{order:0}}
.row-media img{width:100%;border-radius:var(--r-lg);aspect-ratio:4/3;object-fit:cover;
  box-shadow:var(--shadow-2)}
.row h3+p{margin-top:.9rem;color:var(--ink-2)}
.dark .row h3+p{color:var(--on-dark-2)}
.ticks{margin-top:1.4rem;display:grid;gap:.65rem}
.ticks li{display:grid;grid-template-columns:auto 1fr;gap:.7rem;align-items:start;
  font-size:.9688rem}
.ticks svg{width:18px;height:18px;margin-top:.24rem;stroke:var(--orange);stroke-width:2;
  fill:none;stroke-linecap:round;stroke-linejoin:round}

/* ── numbered/def list without the 01/02/03 costume ────────────────────── */
.defs{display:grid;gap:clamp(1.5rem,3vw,2.25rem)}
.def{display:grid;gap:.55rem;padding-top:1.5rem;border-top:1px solid var(--hair)}
.dark .def{border-top-color:var(--hair-dark)}
.def p{color:var(--ink-2);max-width:60ch}
.dark .def p{color:var(--on-dark-2)}
@media (min-width:760px){.defs.two{grid-template-columns:1fr 1fr;gap:2.25rem 3rem}}
@media (min-width:900px){.defs.three{grid-template-columns:1fr 1fr 1fr;gap:2.25rem 2.5rem}}

/* ── client wall — typographic, not logo soup ──────────────────────────── */
/* A static logo grid, for pages that show a subset of clients. The marquee is the
   home page's job; a second travelling strip on a market page is motion for its own
   sake, and these are the same cells standing still. */
.logo-grid{display:flex;flex-wrap:wrap;align-items:center;
  gap:clamp(1.6rem,3.5vw,2.9rem)}
.logo-grid li{display:flex;align-items:center}
.logo-grid img{height:44px;width:auto;max-width:150px;object-fit:contain;opacity:.9}
.logo-grid .logo-type{white-space:nowrap;font-weight:700;letter-spacing:-.02em;
  font-size:clamp(1rem,1.7vw,1.3rem);color:var(--on-dark-2)}

/* ── client logo marquee ───────────────────────────────────────────────── */
/* Two rows travelling in opposite directions. Each row holds the same track twice,
   so translating one full track width lands the copy exactly where the original
   started and the loop has no seam. Everything here is decoration: .wall-quiet
   below carries the names for anyone not watching it move. */
.logos{--logo-gap:clamp(2rem,4.5vw,3.75rem);display:grid;gap:clamp(1rem,2.2vw,1.75rem)}
/* No gap on the row: the join between the two tracks is spaced by the second
   track's own padding-left, so adding a row gap doubles it and the loop shows
   a visible seam every time it comes round. */
.logos-row{display:flex;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.logos-track{display:flex;align-items:center;flex:0 0 auto;gap:var(--logo-gap);
  margin:0;padding:0 0 0 var(--logo-gap);list-style:none;
  animation:logos-travel var(--dur,60s) linear infinite;will-change:transform}
.logos-rev .logos-track{animation-direction:reverse}
@keyframes logos-travel{to{transform:translateX(-100%)}}
/* Hover and keyboard focus both stop it, so nothing a reader wants to look at
   slides away while they are looking at it. */
.logos:hover .logos-track,.logos:focus-within .logos-track,
.logos[data-paused="true"] .logos-track{animation-play-state:paused}
/* A real control, because hover is not a thing on a phone and this row carries the
   client names — the most persuasive content on the site. */
.logos{position:relative}
.logos-toggle{position:absolute;right:0;top:50%;transform:translateY(-50%);z-index:2;
  width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--edge-dark);border-radius:50%;background:var(--ground);
  color:var(--on-dark);cursor:pointer;
  transition:background .18s var(--ease-out),border-color .18s var(--ease-out)}
.logos-toggle:hover{background:var(--ground-lift)}
.logos-toggle:active{transform:translateY(-50%) scale(.94)}
.logos-toggle svg{width:16px;height:16px;fill:currentColor;stroke:none}
.logos-toggle .ico-play{display:none}
.logos[data-paused="true"] .logos-toggle .ico-play{display:block}
.logos[data-paused="true"] .logos-toggle .ico-pause{display:none}
.logos-track li{display:flex;align-items:center}
/* Every mark in this strip is pre-baked white-mono (tools + scratchpad bake), so
   the greyscale-brighten filter the text-and-white era needed would now only crush
   knockout panels — it lifted a coloured panel to light grey and left its white
   text at 1.3:1. Taller too: an all-logo wall earns more height than a text row. */
.logos-track img{height:44px;width:auto;max-width:156px;object-fit:contain;opacity:.85}
/* Our typeface, not a facsimile of theirs — see _client_cell() in pages.py. */
.logo-type{white-space:nowrap;font-weight:700;letter-spacing:-.02em;
  font-size:clamp(1.05rem,1.9vw,1.45rem);color:var(--on-dark-2)}   /* 9.91:1 on --ground ✓ */

/* The names as content. Visually hidden while the marquee runs, and the only
   version anyone gets on reduced motion. */
.wall-quiet{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}
@media (prefers-reduced-motion:reduce){
  .logos{display:none}
  .wall-quiet{position:static;width:auto;height:auto;overflow:visible;
    clip:auto;clip-path:none;white-space:normal}
}

.wall{display:grid;gap:clamp(1.75rem,3.5vw,2.75rem)}
.wall-group h3{font-size:.8125rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--on-dark-2);margin-bottom:.9rem}
.wall-names{display:flex;flex-wrap:wrap;gap:.5rem .9rem}
.wall-names li{font-size:clamp(1.05rem,1.9vw,1.5rem);font-weight:700;letter-spacing:-.02em;
  color:var(--on-dark);line-height:1.25}
.wall-names li+li::before{content:"·";color:var(--orange);margin-right:.9rem;font-weight:700}

/* ── membership strip ──────────────────────────────────────────────────── */
.marks{display:flex;flex-wrap:wrap;align-items:center;gap:clamp(1.5rem,4vw,3rem);
  justify-content:flex-start}
.marks li{display:flex;align-items:center}
/* Capped on width as well as height. These marks run from 1:1 (Sedex) to 4.6:1
   (Qantas), so normalising on height alone rendered Qantas at 156px beside Sedex
   at 34px and the strip read as a size chart. */
.marks img{height:42px;width:auto;max-width:150px;object-fit:contain;opacity:.75;filter:grayscale(1) brightness(1.9) contrast(.85);
  transition:opacity .25s,filter .25s}
.marks li:hover img{opacity:1;filter:none}
.marks-light img{filter:grayscale(1) contrast(.9);opacity:.6}
.marks-light li:hover img{opacity:1;filter:none}

/* ── product grid ──────────────────────────────────────────────────────── */
/* The renders are shot on CPE's own palette — magenta, orange, plum, cream. Putting
   them in white boxes made the coloured ones read as mistakes. Full-bleed tiles let the
   palette do the work and the grid reads as one system. `figure` carries a 40px UA
   margin that silently ate a third of every grid cell; margin:0 is not optional. */
.prods{display:grid;grid-template-columns:repeat(auto-fill,minmax(172px,1fr));gap:.85rem}
/* Half the product renders are near-white on a near-white ground, so without a
   boundary the grid read as a checkerboard of floating objects. */
.prod{margin:0;border-radius:var(--r);overflow:hidden;background:var(--paper-tint);
  border:1px solid var(--hair);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.prod:hover{transform:translateY(-3px);box-shadow:var(--shadow-2)}
.prod img{aspect-ratio:1;object-fit:cover;width:100%;display:block}
.prod figcaption{padding:.6rem .8rem .75rem;font-size:.8125rem;color:var(--ink-2);
  background:var(--paper);min-height:2.9em}
.dark .prod figcaption{background:var(--ground-lift);color:var(--on-dark-2)}

/* ── figure / placeholder honesty ──────────────────────────────────────── */
figure{margin:0}
.figure{margin:0}
.figure img{border-radius:var(--r-lg);width:100%;box-shadow:var(--shadow-2)}
.figure figcaption{margin-top:.7rem;font-size:.8125rem;color:var(--ink-2)}
.dark .figure figcaption{color:var(--on-dark-2)}
.ph-note{display:inline-flex;align-items:center;gap:.4rem;font-size:.75rem;font-weight:600;
  letter-spacing:.02em;color:var(--ink-2);background:var(--paper-tint);
  border:1px solid var(--hair);border-radius:999px;padding:.25rem .7rem}
.dark .ph-note{background:var(--ground-lift);border-color:var(--hair-dark);color:var(--on-dark-2)}

/* ── quote ─────────────────────────────────────────────────────────────── */
.quote{border-left:1px solid var(--orange);padding-left:clamp(1.25rem,3vw,2rem)}
.quote p{font-size:clamp(1.2rem,2.2vw,1.7rem);font-weight:600;letter-spacing:-.02em;
  line-height:1.35}
.quote cite{display:block;margin-top:.9rem;font-style:normal;font-size:.9375rem;
  color:var(--ink-2)}
.dark .quote cite{color:var(--on-dark-2)}

/* ── CTA band ──────────────────────────────────────────────────────────── */
.cta{display:grid;grid-template-columns:1.2fr .8fr;gap:clamp(1.5rem,4vw,3rem);align-items:center}
@media (max-width:800px){.cta{grid-template-columns:1fr}}

/* ── form ──────────────────────────────────────────────────────────────── */
.form{display:grid;gap:1.1rem}
.field{display:grid;gap:.42rem}
.field label{font-size:.875rem;font-weight:600;letter-spacing:-.005em}
.field .hint{font-size:.8125rem;color:var(--ink-2)}
/* Only 'Optional' was ever shown, so required was conveyed by its absence —
   which is not conveyed at all. aria-hidden because the input's own `required`
   already tells assistive tech; this is for the eye. */
.field .req{font-size:.6875rem;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--ink-2);margin-left:.4rem}
.dark .field .req{color:var(--on-dark-2)}
.dark .field .hint{color:var(--on-dark-2)}
.field input,.field select,.field textarea{width:100%;min-height:50px;padding:.75rem .95rem;
  border:1.5px solid var(--edge);border-radius:11px;background:#fff;color:var(--ink);
  transition:border-color .16s,box-shadow .16s}
.field textarea{min-height:130px;resize:vertical;line-height:1.55}
.field input:hover,.field select:hover,.field textarea:hover{border-color:var(--ink-2)}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;
  border-color:var(--orange);box-shadow:0 0 0 3px var(--orange-soft)}
.field input:user-invalid,.field textarea:user-invalid{border-color:#B3261E}
.field .err{font-size:.8125rem;color:#B3261E;font-weight:600}
.two-up{display:grid;grid-template-columns:1fr 1fr;gap:1.1rem}
@media (max-width:640px){.two-up{grid-template-columns:1fr}}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* ── footer ────────────────────────────────────────────────────────────── */
.foot{background:var(--ground-deep);color:var(--on-dark);padding-block:clamp(3rem,6vw,4.5rem) 2rem}
.foot-grid{display:grid;grid-template-columns:1.4fr 1fr 1fr 1fr;gap:2.5rem}
@media (max-width:900px){.foot-grid{grid-template-columns:1fr 1fr}}
@media (max-width:560px){.foot-grid{grid-template-columns:1fr}}
.foot h2{font-size:.8125rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  color:var(--on-dark-2);margin-bottom:.9rem}
.foot a{text-decoration:none;color:var(--on-dark);font-size:.9375rem;
  display:inline-block;padding-block:.76rem;line-height:1.35}
.foot a:hover{color:var(--orange)}
.foot-brand img{width:auto;height:40px;margin-bottom:1.1rem}
.foot-brand p{color:var(--on-dark-2);font-size:.9375rem;max-width:34ch}
.foot-base{margin-top:clamp(2.5rem,5vw,3.5rem);padding-top:1.5rem;
  border-top:1px solid var(--hair-dark);display:flex;flex-wrap:wrap;gap:.6rem 1.5rem;
  align-items:center;color:var(--on-dark-2);font-size:.8125rem}
.foot-base a{font-size:.8125rem;color:var(--on-dark-2)}
.foot-base .sep{margin-left:auto}

/* ── breadcrumb ────────────────────────────────────────────────────────── */
.crumb{display:flex;flex-wrap:wrap;gap:.4rem;align-items:center;font-size:.8125rem;
  color:var(--ink-2);padding-top:1.6rem}
.crumb a{min-height:44px;display:inline-flex;align-items:center;text-decoration:none;color:var(--ink-2);display:inline-block;padding-block:.7rem}
.crumb a:hover{color:var(--orange-ink);text-decoration:underline}
.crumb svg{width:13px;height:13px;stroke:currentColor;stroke-width:2;fill:none;opacity:.6}
.dark .crumb,.dark .crumb a{color:var(--on-dark-2)}

/* ── page header (interior) ────────────────────────────────────────────── */
.phead{background:var(--ground);color:var(--on-dark);
  padding-block:clamp(1rem,2vw,1.5rem) clamp(3rem,6vw,5rem)}
.phead h1{margin-top:clamp(1.25rem,3vw,2rem);color:var(--on-dark)}
/* Every sub-brand lockup reverses to white on the dark hero. Left in its own colour the
   Creative Locker's #AC004F sits near 1.6:1 against the ground and is unreadable; Grand
   Harvest's gradient fares no better. Reversing is the standard treatment for a lockup on
   a dark ground and it keeps the four consistent. */
.phead-logo{filter:brightness(0) invert(1);opacity:.96;
  margin-top:clamp(1.25rem,3vw,2rem)}
.phead .lede{margin-top:1.25rem;color:var(--on-dark-2);max-width:56ch}

/* ── insights ──────────────────────────────────────────────────────────── */
.posts{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1.5rem}
.post{display:flex;flex-direction:column;background:var(--paper);border:1px solid var(--hair);
  border-radius:var(--r-lg);overflow:hidden;text-decoration:none;
  transition:transform .28s var(--ease),box-shadow .28s var(--ease)}
.post:hover{transform:translateY(-3px);box-shadow:var(--shadow-3)}
.post img{aspect-ratio:16/10;object-fit:cover;width:100%}
.post-b{padding:1.35rem 1.4rem 1.5rem;display:flex;flex-direction:column;flex:1}
.post-b time{font-size:.8125rem;color:var(--ink-2)}
.post-b .h3{margin-top:.5rem}
.post-b p{margin-top:.6rem;color:var(--ink-2);font-size:.9375rem}
/* One measure for the whole article. The header wrap, the hero figure and the body
   each used to set their own, and the .wrap gutter was eaten from inside the 74ch,
   so the headline sat 44px in from the paragraph it introduces. */
:root{--article:74ch}
.article{max-width:var(--article);margin-inline:auto}
.article-head{max-width:calc(var(--article) + 2 * var(--gut))}
.article h2{margin-top:2.75rem;font-size:clamp(1.5rem,2.6vw,2rem)}
.article h3{margin-top:2rem;font-size:1.25rem;font-weight:600}
.article p{margin-top:1.15rem}
.article ul{margin-top:1.15rem;display:grid;gap:.6rem}
.article ul li{display:grid;grid-template-columns:auto 1fr;gap:.7rem;align-items:start}
.article ul li::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--orange);
  margin-top:.62rem}

/* ── utility ───────────────────────────────────────────────────────────── */
.center{text-align:center}
.mt-0{margin-top:0}
.hide{display:none}
@media (max-width:700px){.hide-s{display:none}}

/* ── press feedback ────────────────────────────────────────────────────── */
/* The only motion added after the sweep. Cards, ghost buttons and the burger were
   the sole interactive elements with no :active state, which on a touch screen reads
   as a dead tap. Feedback, 140ms, subtle enough not to feel springy at this frequency.
   Rejected in the same pass: a stagger on the product grid (the section reveal already
   covers it, and doubling up is the scattered-effects habit), and any motion on the
   nav links (tens of interactions a day — animation there makes the site feel slower). */
.biz-card,.post,.prod{transition:transform .3s var(--ease),box-shadow .3s var(--ease),
  border-color .3s}
.biz-card:active,.post:active{transform:translateY(-1px) scale(.994)}
.prod:active{transform:scale(.985)}
.btn-ghost:active{transform:translateY(1px)}
.burger:active{transform:scale(.94)}
.burger{transition:transform .14s var(--ease-out),border-color .18s}
@media (hover:hover) and (pointer:fine){.burger:hover{border-color:var(--ink)}}

/* ── reveal — one quiet entrance, not a circus ─────────────────────────── */
/* Gated on .js, which an inline head script sets before first paint. Without that
   gate, a JS failure leaves every revealed section permanently invisible and the
   page reads as blank below the hero. */
@media (prefers-reduced-motion:no-preference){
  .js .rv{opacity:0;transform:translateY(14px);
    transition:opacity .7s var(--ease-out),transform .7s var(--ease)}
  .js .rv.in{opacity:1;transform:none}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
}

/* ── print ─────────────────────────────────────────────────────────────── */
/* Print doubles as the source for the downloadable capability statement, rendered by
   tools/make-capability-pdf.py. Anything that only makes sense on a screen — nav,
   the download button that produced the file, calls to action — comes out. */
@page{margin:16mm 14mm}
@media print{
  .mast,.foot,.burger,.btns,.cta,[data-dl],.logos{display:none!important}
  /* The marquee cannot travel on paper; print the names instead. */
  .wall-quiet{position:static!important;width:auto!important;height:auto!important;
    overflow:visible!important;clip:auto!important;clip-path:none!important;
    white-space:normal!important}
  body{background:#fff;color:#000;font-size:11pt}
  .band{padding-block:1rem}
  .dark,.deep{background:#fff!important;color:#000!important}
  .dark h1,.dark h2,.dark h3,.wall-names li{color:#000!important}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:9pt;color:#555}
  /* Reveal-on-scroll starts hidden; a headless render never scrolls, so pin it open. */
  .rv{opacity:1!important;transform:none!important}
  h1,h2,h3{break-after:avoid-page}
  figure,.prod,.card,tr{break-inside:avoid-page}
  section{break-inside:auto}
}
