:root{
    /* DDX Consulting — light. Dixon's call: white reads better for a
       consultancy, and it is what the mock he approved is built on.
       Every value here was measured against its own surface, not eyeballed:
       the pair after each is contrast on #FFFFFF and on --card. */
    /* The brand palette, as Dixon supplied it:
     *
     *   #0B0F19   #101827   #1D2638   #5D728E   #E6E6E6   #FFFFFF
     *
     * Every colour below is one of those six, or a straight neutral step
     * between two of them (#FFFFFF-#E6E6E6 for surfaces, #5D728E-#E6E6E6
     * for rules). No new hue is introduced anywhere — the pair after each
     * value is its contrast on #FFFFFF and on --card. */
    --paper:#FFFFFF; --card:#F1F1F2; --raised:#E6E6E6;
    --ink:#0B0F19; --ink-hover:#0B0F19;                 /* 19.4 / 18.3 */
    --dim:#1D2638;                                      /* 15.0 / 14.2 */
    --faint:#44536A;                                    /*  8.5 /  8.0 */
    --line:#E6E6E6; --line-soft:#F1F1F2; --line-strong:#C2C9D2;
    /* #5D728E is 4.64 on paper — a rule, a wash and a fill, but never small
       type on a tinted surface, where it drops under 4.5. */
    --steel:#1D2638;                                    /* 15.0 / 14.2 */
    --steel-hi:#0B0F19;                                 /* 19.4 / 18.3 */
    --steel-dim:#5D728E;
    --steel-wash:#E6E6E6;
    /* legacy aliases kept so inline styles in the markup keep resolving */
    --cy:#1D2638; --green:#1D2638; --mint:#0B0F19; --red:#8E3325;
    --panel-dim:#1D2638; --panel-faint:#44536A; --panel-eyebrow:#1D2638;
  }
  *{box-sizing:border-box}
  /* Selecting text used to draw the browser's default highlight, a bright
     blue that belongs to no part of this palette and reads as a rendering
     fault on the security page, where people select passages to quote.
     --steel-dim is a mid-tone on purpose: it stays visible on the white
     pages and on the dark hero, and white on it measures 4.64. */
  ::selection{background:var(--steel-dim);color:#FFFFFF}
  ::-moz-selection{background:var(--steel-dim);color:#FFFFFF}
  html{scroll-behavior:smooth;background:var(--paper)}
  body{margin:0;background:var(--paper);color:var(--ink);font:15px/1.65 "Switzer","Neue Montreal",system-ui,"Segoe UI",sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}
  a{color:var(--steel);text-decoration:none;transition:color .15s ease}
  a:hover{color:var(--steel-hi)}
  h1,h2{font-family:inherit;font-weight:500;letter-spacing:-.025em;text-wrap:pretty;margin:0;color:var(--ink)}
  .wrap{max-width:1160px;margin:0 auto;padding:0 32px}
  .mono{font-family:"IBM Plex Mono",ui-monospace,monospace}
  .eyebrow{font-size:11px;font-weight:500;letter-spacing:.18em;color:var(--steel);text-transform:uppercase}
  .btn{display:inline-flex;align-items:center;justify-content:center;height:46px;padding:0 26px;background:var(--steel);color:#FFFFFF;border-radius:2px;font-size:13px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;border:none;cursor:pointer;transition:background .15s ease}
  .btn:hover{background:var(--steel-hi);color:#FFFFFF}
  .btn-ghost{display:inline-flex;align-items:center;justify-content:center;height:46px;padding:0 24px;background:transparent;color:var(--ink);border:1px solid var(--line-strong);border-radius:2px;font-size:13px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;cursor:pointer;transition:border-color .15s ease,color .15s ease}
  .btn-ghost:hover{border-color:var(--steel);color:var(--steel)}
  .ulink{font-size:13px;font-weight:500;letter-spacing:.1em;text-transform:uppercase;color:var(--steel);border-bottom:1px solid rgba(107,139,164,.4);padding-bottom:3px}
  .ulink:hover{color:var(--steel-hi);border-color:var(--steel-hi)}

  /* brand mark */
  .ddx-mark{display:block;flex-shrink:0}
  .brand{display:flex;align-items:center;gap:12px;text-decoration:none}
  .brand .ddx-word{display:block;width:118px;height:auto}
  @media (max-width:640px){.brand .ddx-word{width:101px}}
  .brand .rule{width:1px;height:34px;background:linear-gradient(180deg,#1B2431,#6B8BA4,#1B2431);flex-shrink:0}

  /* nav */
  /* The bar was fully transparent until the page scrolled. That was fine over
     a dark photograph and is not over a field of marks — the links sat
     straight on the pattern with nothing dividing them from it. It carries
     its own band and rule from the first frame now, and still deepens on
     scroll. The docked colour was also still the old paper. */
  nav{position:fixed;top:0;left:0;right:0;z-index:20;
    background:linear-gradient(180deg,rgba(255,255,255,.95),rgba(255,255,255,.78));
    -webkit-backdrop-filter:blur(7px);backdrop-filter:blur(7px);
    border-bottom:1px solid rgba(203,211,220,.9);
    transition:background .25s ease,border-color .25s ease,backdrop-filter .25s ease}
  nav.docked{background:rgba(255,255,255,.96);backdrop-filter:blur(12px);
    border-bottom-color:var(--line-strong)}
  .nav-in{height:70px;display:flex;align-items:center;justify-content:space-between}
  .nav-links{display:flex;align-items:center;gap:30px;font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase}
  .nav-links a{color:var(--dim)}
  .nav-links a:hover{color:var(--ink)}
  .nav-cta{display:inline-flex;align-items:center;height:36px;padding:0 20px;background:var(--steel);color:#FFFFFF !important;border-radius:2px;letter-spacing:.12em}
  .nav-cta:hover{background:var(--steel-hi);color:#FFFFFF !important}
  .nav-signin{display:inline-flex;align-items:center;height:36px;padding:0 18px;border:1px solid var(--line-strong);border-radius:2px;color:var(--dim) !important;letter-spacing:.12em}
  .nav-signin:hover{border-color:var(--steel);color:var(--steel) !important}
  @media (max-width:1000px){.nav-links a.hideS{display:none}}
  /* under 700 the bar cannot hold the lockup and two buttons without the
     sign-in wrapping onto the brand — sign-in moves to the footer links */
  @media (max-width:700px){
    .nav-links{gap:14px}
    .nav-signin{display:none}
    .brand .ddx-word{width:97px}
    .nav-cta{height:34px;padding:0 16px;font-size:10.5px}
  }

  /* Under 1000 the section links leave the bar. They have to go somewhere —
     a phone visitor could otherwise only reach Contact. */
  .nav-burger{display:none;align-items:center;justify-content:center;width:38px;height:38px;
    background:transparent;border:1px solid var(--line-strong);border-radius:2px;cursor:pointer;padding:0}
  .nav-burger:hover{border-color:var(--steel)}
  .nav-burger svg{display:block;width:20px;height:20px;color:var(--ink)}
  @media (max-width:1000px){.nav-burger{display:inline-flex}}

  .sheet{position:fixed;inset:0;z-index:30;background:var(--paper);
    display:flex;flex-direction:column;padding:0 20px 30px;
    opacity:0;visibility:hidden;transform:translateY(-6px);
    transition:opacity .22s ease,transform .22s ease,visibility .22s ease}
  .sheet[data-open]{opacity:1;visibility:visible;transform:none}
  .sheet-top{height:70px;flex-shrink:0;display:flex;align-items:center;justify-content:space-between}
  /* the sizing rule for the wordmark is scoped to .brand, which this is not */
  .sheet-top .ddx-word{display:block;width:101px;height:auto}
  .sheet-close{width:38px;height:38px;background:transparent;border:1px solid var(--line-strong);
    border-radius:2px;color:var(--ink);font:400 20px/1 "Switzer",system-ui,sans-serif;cursor:pointer;padding:0}
  .sheet-close:hover{border-color:var(--steel);color:var(--steel)}
  .sheet-links{display:flex;flex-direction:column;margin-top:12px}
  .sheet-links a{display:block;padding:15px 0;font-size:26px;font-weight:500;letter-spacing:-.022em;
    color:var(--ink);border-bottom:1px solid var(--line-soft)}
  .sheet-links a:hover{color:var(--steel)}
  .sheet-foot{margin-top:auto;padding-top:28px;display:flex;flex-direction:column;gap:12px}
  .sheet-foot .btn,.sheet-foot .btn-ghost{width:100%}
  body.menu-open{overflow:hidden}
  @media (max-width:640px){.sheet-top{height:62px}.sheet-links a{font-size:23px;padding:13px 0}}
  @media (max-width:640px){
    .wrap{padding:0 20px}
    .nav-in{height:62px}
    .sec{padding-top:52px;padding-bottom:56px}
    /* the captions eat two thirds of a phone line and leave room for two
       logos. Drop them — a row of recognisable app marks says "it plugs
       into your stack" without being told */
    .works-in{padding:15px 0}
    .works .cap{display:none}
    .river{flex:1 1 100%;min-width:0}
    .river img{width:40px;height:40px;padding:8px}
    .sec h2{margin-bottom:28px}
    .foot{padding:28px 20px}
    .blueprint{padding:20px}
    .bp-out .chips{justify-content:flex-start}
  }


  /* ── the walk ────────────────────────────────────────────────────────
     One corridor, three places along it. The hero sits in it; sections 01
     and 02 each get a full-screen title card cut into it. Everything after
     that is an ordinary document — a buyer looking for the contact form
     should not have to walk there. */
  .plate{position:absolute;inset:0;z-index:0;overflow:hidden}
  .plate img{width:100%;height:100%;object-fit:cover;display:block;will-change:transform}
  /* Barely there. The old scrim ran to .93 across the left of the frame and
     you could see it — the picture looked filtered rather than lit. The
     atrium plate already carries its own dark left third, so all that is
     left to do is stop the type sitting directly on the gold edge. */
  .veil{position:absolute;inset:0;z-index:1;pointer-events:none;background:
    linear-gradient(90deg,rgba(255,255,255,0.49) 0%,rgba(255,255,255,0.19) 34%,transparent 62%)}

  .hero-zone{position:relative;overflow:hidden;min-height:100svh;display:flex;align-items:center;
    border-bottom:1px solid var(--line)}
  .hero-zone .hero{position:relative;z-index:2}

  .chapter{position:relative;height:185vh}
  .chapter-stage{position:sticky;top:0;height:100svh;overflow:hidden;display:grid;align-content:center}
  .chapter-copy{position:relative;z-index:2;width:100%;max-width:1160px;margin:0 auto;padding:0 32px;
    will-change:opacity,transform}
  .chapter-copy .k{font-size:10.5px;font-weight:500;letter-spacing:.24em;text-transform:uppercase;color:var(--steel)}
  .chapter-copy h2{font-size:clamp(30px,5.6vw,58px);line-height:1.04;letter-spacing:-.03em;
    font-weight:500;margin:22px 0 0;max-width:15ch;text-wrap:balance}
  .chapter-copy p{margin:22px 0 0;max-width:500px;font-size:16px;line-height:1.65;color:var(--dim)}
  .chapter-cue{position:absolute;z-index:2;bottom:32px;left:50%;transform:translateX(-50%);
    font-size:9.5px;font-weight:500;letter-spacing:.24em;text-transform:uppercase;color:var(--faint)}

  /* the walk is a desktop pleasure; on a phone it is a scroll tax */
  @media (max-width:820px){
    /* Same lesson as the hero: a portrait crop of this corridor is not a
       readable picture, it is a white wedge on black, and type over it needs
       a scrim that erases whatever was left. So the beat splits — the
       corridor gets a wide, short frame where its perspective survives, and
       the words sit under it on solid ground. */
    .chapter{height:auto}
    .chapter-stage{position:static;height:auto;padding:0;
      display:flex;flex-direction:column;overflow:hidden}
    .chapter .plate{position:relative;inset:auto;order:1;height:210px;
      border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
    .chapter .plate img{object-position:56% center}
    .chapter .veil{display:none}
    .chapter-copy{opacity:1 !important;transform:none !important;
      order:2;padding:26px 20px 34px}
    /* The 90deg veil was cut for a landscape frame: it darkens left-to-right
       so type can sit in the left third. A phone stacks the type instead, so
       that ramp just buries the picture. Run it top-to-bottom, and slide the
       crop toward the lit wall so the corridor still reads at this width. */
    .veil{background:linear-gradient(180deg,
      rgba(255,255,255,0.34) 0%,rgba(255,255,255,0.63) 30%,rgba(255,255,255,0.98) 70%,rgba(255,255,255,0.98) 100%)}
    /* The hero carries type all the way down, so it needs the steeper ramp:
       the light stays in the top band and everything under it goes to ground.
       60% centres the portrait crop on the ceiling slot and the lit edge —
       further right and the bright wall lands behind the headline. */
    .hero-zone .veil{background:
    /* The copy column. On navy this held the words down; on white it lifts
       them off the pattern, which is the same job with the sign flipped —
       a near-solid page under the type, falling to nothing by two thirds
       across so the field is untouched where nothing is written. */
    linear-gradient(90deg, rgba(255,255,255,.97) 0%, rgba(255,255,255,.93) 30%,
                           rgba(255,255,255,.72) 46%, rgba(255,255,255,.24) 62%,
                           transparent 76%),
    /* the facet column on the right gets its own quieter band */
    linear-gradient(270deg, rgba(255,255,255,.88) 0%, rgba(255,255,255,.5) 16%,
                            transparent 32%),
    linear-gradient(180deg, rgba(255,255,255,.62) 0%, rgba(255,255,255,.1) 24%,
                            rgba(255,255,255,.1) 66%, rgba(255,255,255,.72) 100%)}
    /* scoped to chapters: this used to hit the hero plate as well and
       !important beat the inline transform the parallax sets */
    .chapter .plate img{transform:none !important;object-position:60% center}
    .chapter-cue{display:none}
  }
  @media (prefers-reduced-motion:reduce){
    .chapter{height:auto}
    .chapter-stage{position:static;height:auto;padding:72px 0}
    .chapter-copy{opacity:1 !important;transform:none !important}
    .chapter .plate img{transform:none !important}
    .chapter-cue{display:none}
  }

  /* hero */
  /* padding trimmed when the connects-to strip moved into the hero: at 150/96
     the block ran past a 900px-tall laptop and crowded the pillars into the cue */
  .hero{display:block;width:100%;padding-top:110px;padding-bottom:100px}
  .hero h1{font-size:clamp(38px,7.6vw,76px);line-height:1.02;margin-top:28px;letter-spacing:-.032em;max-width:14ch}

  /* ── the set-wide headline ────────────────────────────────────────────
     Light Jost in wide-tracked caps, one statement per line, the last in
     tan. It is the lockup's own typeface at display size, which is why it
     reads as the brand rather than as a font choice. Tracking is a luxury
     of width: it opens up on a desktop and closes again on a phone, where
     letters that far apart stop forming words. */
  .hero h1.setwide{font-family:"Jost",system-ui,sans-serif;font-weight:300;
    text-transform:uppercase;letter-spacing:.055em;line-height:1.13;
    font-size:clamp(29px,5.1vw,62px);max-width:none;margin-top:26px}
  .hero h1.setwide span{display:block}
  .hero h1.setwide .accent{color:var(--steel)}

  /* the offer column, opposite the words */
  /* The column lands on the lit wall, which is the brightest part of the
     plate — the dim body copy washes out there. A scrim local to this
     column keeps it readable without putting the filter back over the
     whole picture. */
  .hero-facets{display:grid;gap:26px;align-content:center;max-width:300px;
    position:relative;padding:22px 20px 24px;margin:0 -20px;
    background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,0.71) 18%,rgba(255,255,255,0.84));
    -webkit-backdrop-filter:blur(1.5px);backdrop-filter:blur(1.5px)}
  .facet{border-top:1px solid var(--line-strong);padding-top:14px;
    display:grid;grid-template-columns:26px 1fr;column-gap:14px;align-items:start}
  .fmark{width:20px;height:20px;grid-row:1/3;margin-top:1px;color:var(--steel)}
  .fmark.solid{color:var(--steel)}
  .facet b{font-size:11.5px;font-weight:600;letter-spacing:.12em;
    text-transform:uppercase;color:var(--ink)}
  .facet span{font-size:13.5px;line-height:1.5;color:var(--dim);margin-top:7px;grid-column:2}
  @media (min-width:1001px){
    .hero{display:grid;grid-template-columns:minmax(0,1fr) 290px;gap:70px;align-items:center}
  }
  @media (max-width:1000px){.hero-facets{display:none}}
  .hero-cue{position:absolute;z-index:3;bottom:30px;left:50%;transform:translateX(-50%);
    font-size:9.5px;font-weight:500;letter-spacing:.26em;text-transform:uppercase;color:var(--faint)}
  .hero-cue::after{content:"";display:block;width:1px;height:26px;margin:10px auto 0;
    background:linear-gradient(180deg,var(--steel),transparent)}
  .hero .sub{font-size:17px;line-height:1.65;color:var(--dim);margin:26px 0 0;max-width:520px}
  .hero-ctas{display:flex;align-items:center;gap:24px;margin-top:38px;flex-wrap:wrap}
  .hero-note{font-size:10.5px;font-weight:500;letter-spacing:.16em;color:var(--faint);margin-top:20px;text-transform:uppercase}
  /* DATA · PROCESS · AI */
  .pillars{display:flex;gap:18px;flex-wrap:wrap;margin-top:34px;padding-top:22px;border-top:1px solid var(--line)}
  .pillars span{font-size:10.5px;font-weight:500;letter-spacing:.24em;color:var(--faint);text-transform:uppercase}

  /* Phone hero. This has to sit AFTER the block above: the earlier
     max-width:640px rules are same-specificity and lose the cascade to it.
     Bottom-anchored, so the copy lands in the dark half of the vertical veil
     and the top third of the frame stays a photograph. Centred and at desktop
     leading it filled the viewport edge to edge, with no room for the cue. */
  /* the cascade is a phone device only — desktop already has a real nav */
  .hero-index{display:none}
  /* the phone eyebrow is an index rule with a short label; everywhere else
     the full sentence runs on its own. Only ever one of the two is shown. */
  /* likewise the sub: one short sentence on a phone, the full paragraph
     everywhere else. Never both. */
  .hero .sub .sub-lead{display:none}

  /* ── Phone hero ───────────────────────────────────────────────────────
     Rebuilt against how mobile heroes actually work: the picture owns the
     top of the frame and stays legible, the words sit in a solid slab at
     the bottom, and there is ONE button near the thumb.

     The old one carried eight things — eyebrow, three-line headline, a
     five-line paragraph, two calls to action, a note, four pillars, a
     twenty-two mark logo strip and a scroll cue. Type needed about 70% of
     the frame, which left the corridor as an unreadable dark texture. The
     fix is not smaller type, it is fewer elements: the strip, pillars,
     note and eyebrow all have somewhere better to be further down. */
  @media (max-width:640px){
    /* Type first, on solid ground. The corridor is a dark, low-contrast
       photograph: used as a backdrop it forces a scrim heavy enough to
       erase itself, which is why the old phone hero read as a black
       rectangle. It goes to the foot of the cascade instead, framed, where
       it can be lit. */
    /* The reference stacks its cards on a coloured panel — that ground is
       what makes them read as a deck rather than as misaligned bars. This
       palette has no second colour to spare, but it has the corridor: the
       photograph becomes the panel, and solid dark cards step across it. */
    .hero-zone{align-items:flex-start;min-height:0;background:var(--paper);border-bottom:0}
    .hero-zone .plate,.hero-zone .veil{display:none}
    .hero{padding-top:80px;padding-bottom:30px}
    /* the note stays: it is the promise under the button, and hiding it on
       the screen most visitors arrive on hid it from most visitors */
    .hero .pillars,.hero .works,.hero-cue{display:none}
    .hero .sub .sub-rest{display:none}
    .hero .sub .sub-lead{display:inline}


    .hero h1{font-size:33px;line-height:1.08;letter-spacing:-.028em;margin-top:16px;max-width:13ch}
    /* tracking closes right down here — at .055em these caps stop reading as
       words on a 390px column */
    .hero h1.setwide{font-size:29px;letter-spacing:.012em;line-height:1.2;margin-top:14px}
    .hero .sub{font-size:15.5px;line-height:1.55;margin-top:14px;max-width:none}
    .hero-ctas{display:grid;grid-template-columns:1fr;gap:0;margin-top:22px}
    .hero-ctas .btn{width:100%;height:54px;justify-content:center;font-size:14px}
    .hero-ctas .ulink{justify-self:center;margin-top:14px;min-height:44px;
      display:inline-flex;align-items:center;border-bottom:0;font-size:13px;color:var(--dim)}

    /* ── index + one picture ─────────────────────────────────────────────
       The cascading-card pattern was tried here and abandoned: it works in
       the reference because each card is a different hue, and this palette
       is one near-black plus a tan. Offset dark-on-dark cards do not read as
       a stack, they read as a broken grid. What survives is the useful part
       — a visible table of contents instead of a hamburger and nothing —
       set flat, with the corridor as the single framed picture on the page. */
    /* Words on solid ground, then one picture that runs off the right edge.
       Nothing overlaps: the corridor is finally lit because it never has to
       sit under type. The cascading-deck pattern was tried and dropped —
       it needs a second brand colour to read, and it carries a hospitality
       register this business does not want. */
    .hero-index{display:block;margin:30px 0 0}
    .hix{display:none}
    .hix-plate{display:block;height:250px;position:relative;overflow:hidden;
      margin-right:-20px;border-top-left-radius:4px;border-bottom-left-radius:4px;
      border:1px solid var(--line);border-right:0}
    .hix-plate img{width:100%;height:100%;object-fit:cover;object-position:52% center;display:block}
    .hix-plate .cap{position:absolute;left:18px;bottom:16px;font-size:12px;letter-spacing:.08em;
      color:var(--ink);text-transform:uppercase;text-shadow:0 1px 14px rgba(255,255,255,0.98)}

    /* One photographic moment per page. The chapter cards carry the headings
       for the sections beneath them, so they stay — but as headings, not as
       three more heroes in a row. The corridor appears once, in the hero
       cascade, and that is the whole of it. */
    .chapter .plate{display:none}
    .chapter-stage{border-top:1px solid var(--line)}
    .chapter-copy{padding:34px 20px 4px}
    .chapter+section,.chapter+.sec{padding-top:18px}
  }
  .pillars span::before{content:"";display:inline-block;width:4px;height:4px;background:var(--steel);margin-right:10px;vertical-align:middle;transform:rotate(45deg)}

  /* ruled card */
  .rcard{background:var(--card);border:1px solid var(--line);border-radius:2px}
  .rcard-head{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:15px 18px;border-bottom:1px solid var(--line);font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;letter-spacing:.1em;color:var(--faint);text-transform:uppercase}
  .rrow{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:13px 18px;border-bottom:1px solid var(--line-soft);font-size:13px;color:var(--dim)}
  .rrow:last-child{border-bottom:0}
  .rrow .st{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10px;letter-spacing:.08em;white-space:nowrap}
  .stamp{display:inline-block;border:1px solid var(--steel);color:var(--steel);border-radius:2px;padding:7px 12px;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10px;letter-spacing:.12em;transform:rotate(-3deg);text-transform:uppercase}

  /* works-with river */
  .works{border-top:1px solid var(--line);background:var(--card)}
  .works-in{display:flex;align-items:center;gap:22px;padding:18px 32px}
  .works .cap{font-size:10px;font-weight:500;letter-spacing:.16em;color:var(--faint);white-space:nowrap;text-transform:uppercase}
  .river{flex:1;min-width:0;overflow:hidden;-webkit-mask-image:linear-gradient(90deg,transparent,black 8%,black 92%,transparent);mask-image:linear-gradient(90deg,transparent,black 8%,black 92%,transparent)}
  .river-track{display:flex;gap:10px;width:max-content;will-change:transform}
  .river img{width:42px;height:42px;border-radius:6px;background:#FFFFFF;border:1px solid var(--line);padding:9px;flex-shrink:0}
  /* Inside the hero the strip is not a band across the page: no card, no rule,
     no page gutter, and held to the measure of the copy so it does not run out
     over the corridor. The river's own edge mask does the truncating. */
  /* Wider than the copy on purpose: the strip is a list, not a sentence, so it
     runs the full measure of the page rather than the measure of the paragraph.
     The river's own edge mask does the truncating. */
  .hero .works{background:transparent;border-top:0;margin-top:30px}
  .hero .works-in{padding:0;gap:16px}
  .hero .works .cap{font-size:9.5px}
  .hero .river img{width:38px;height:38px;padding:8px}
  @keyframes river{from{transform:translateX(0)}to{transform:translateX(-50%)}}
  @media (prefers-reduced-motion:reduce){.river-track{animation:none !important}}

  /* sections */
  section{border-top:1px solid var(--line)}
  section[id],div[id]{scroll-margin-top:84px}
  .sec{padding-top:72px;padding-bottom:80px}
  .sec h2{font-size:clamp(26px,3.4vw,38px);line-height:1.14;margin:18px 0 40px;max-width:660px;letter-spacing:-.028em}
  .sec h2.tight{margin-bottom:0}

  /* spec table */
  .spec .cols{display:grid;grid-template-columns:260px 1fr 200px;padding:13px 20px;border-bottom:1px solid var(--line);font-size:10px;font-weight:500;letter-spacing:.14em;color:var(--faint);text-transform:uppercase}
  .spec .row{display:grid;grid-template-columns:260px 1fr 200px;padding:22px 20px;border-bottom:1px solid var(--line-soft);font-size:14px;align-items:start}
  .spec .row:last-child{border-bottom:0}
  .spec .row b{font-weight:500;color:var(--ink);font-size:15px}
  .spec .row .what{color:var(--dim);line-height:1.6;padding-right:32px}
  .spec .row .tag{font-size:10px;font-weight:500;color:var(--steel);letter-spacing:.14em;text-transform:uppercase}
  @media (max-width:820px){.spec .cols{display:none}.spec .row{grid-template-columns:1fr;gap:10px}}

  /* how — three ruled columns */
  .steps3{display:grid;grid-template-columns:1fr 1fr 1fr;border-top:1px solid var(--line)}
  .steps3>div{padding:28px 32px 0 0}
  .steps3>div+div{padding-left:32px;border-left:1px solid var(--line)}
  .steps3 .stepicon{display:block;margin-bottom:16px;opacity:.9}
  .steps3 .k{font-size:10px;font-weight:500;color:var(--steel);letter-spacing:.18em;text-transform:uppercase}
  .steps3 h3{font-size:18px;font-weight:500;margin:14px 0 10px;color:var(--ink);letter-spacing:-.015em}
  .steps3 p{font-size:14px;line-height:1.65;color:var(--dim);margin:0}
  @media (max-width:820px){.steps3{grid-template-columns:1fr}.steps3>div+div{padding-left:0;border-left:0;border-top:1px solid var(--line);margin-top:26px;padding-top:26px}}

  /* blueprint */
  .blueprint{border:1px solid var(--line);border-radius:2px;background-color:var(--card);background-image:linear-gradient(rgba(107,139,164,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(107,139,164,.045) 1px,transparent 1px);background-size:24px 24px;padding:32px;overflow-x:auto}
  .bp-head{display:flex;justify-content:space-between;align-items:baseline;gap:16px;margin-bottom:28px;font-family:"IBM Plex Mono",ui-monospace,monospace}
  .bp-head .fig{font-size:11px;letter-spacing:.1em;color:var(--steel);text-transform:uppercase}
  .bp-head .note{font-size:10.5px;color:var(--faint)}
  .bp-svg{overflow-x:auto}
  .bp-svg svg{display:block;min-width:940px;width:100%;height:auto}
  .bp-flow{display:none;align-items:stretch}
  .node{flex:1;border:1px solid var(--line-strong);border-radius:2px;background:var(--raised);padding:14px}
  .node.decision{border-style:dashed}
  .node.human{flex:1.1;border:1px solid var(--steel);background:rgba(107,139,164,.06)}
  .node.done{border:1px solid var(--steel);background:var(--raised)}
  .node.done .k{color:var(--steel)}
  .node .k{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:9.5px;letter-spacing:.12em;color:var(--faint);text-transform:uppercase}
  .node.human .k{color:var(--steel)}
  .node b{display:block;font-size:13px;font-weight:500;margin-top:7px;color:var(--ink)}
  .node .d{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;color:var(--dim);margin-top:5px}
  .edge{width:28px;display:flex;align-items:center;flex-shrink:0}
  .edge i{display:block;height:1px;width:100%;background:var(--line-strong)}
  .bp-loops{margin-top:16px;display:grid;gap:8px}
  .loop{display:flex;gap:10px;align-items:baseline;border:1px dashed var(--line-strong);border-radius:2px;background:var(--raised);padding:10px 13px;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;color:var(--dim);flex-wrap:wrap}
  .loop .lk{color:var(--faint);letter-spacing:.1em;white-space:nowrap;text-transform:uppercase}
  .loop b{color:var(--ink);font-weight:400}
  .loop .ret{color:var(--steel);white-space:nowrap}
  .bp-out{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-top:20px;flex-wrap:wrap}
  .bp-out .lead{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;letter-spacing:.1em;color:var(--steel);white-space:nowrap;text-transform:uppercase}
  .bp-out .chips{display:flex;gap:12px;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;color:var(--ink);flex-wrap:wrap}
  .bp-out .chips span{border:1px solid var(--line);background:var(--raised);border-radius:2px;padding:9px 12px}
  .fig-tabs{display:flex;gap:10px;margin-bottom:26px;font-size:10.5px;font-weight:500;letter-spacing:.1em;flex-wrap:wrap}
  .fig-tab{background:transparent;border:1px solid var(--line-strong);border-radius:2px;cursor:pointer;padding:11px 16px;font:inherit;color:var(--dim);text-transform:uppercase;transition:border-color .15s ease,color .15s ease,background .15s ease}
  .fig-tab:hover{border-color:var(--steel);color:var(--steel)}
  .fig-tab.on{background:var(--steel);border-color:var(--steel);color:#FFFFFF}
  .fig-board{display:none}
  .fig-board.on{display:block}
  @media (min-width:821px){.bp-out{display:none}}
  @media (max-width:820px){
    .fig-tabs{gap:8px}
    .fig-tab{flex:1 1 100%;text-align:left}
    .blueprint{padding:18px}
    .bp-svg{display:none}
    .bp-flow{display:flex;flex-direction:column;min-width:0}
    .node{flex:none}
    .node.human{flex:none}
    .edge{width:auto;height:22px;padding-left:26px;align-items:stretch}
    .edge i{width:1px;height:100%}
    .bp-head{flex-direction:column;align-items:flex-start;gap:4px;margin-bottom:20px}
    .bp-out{margin-top:16px;justify-content:flex-start}
    .bp-out .chips{flex-direction:column;width:100%;gap:8px}
  }

  /* case record — raised band */
  .case-band{background:var(--card)}
  .case-band .eyebrow{color:var(--steel)}
  .case-grid{display:grid;grid-template-columns:1fr 360px;gap:64px;align-items:start;margin-top:18px}
  @media (max-width:860px){.case-grid{grid-template-columns:1fr}}
  .case-grid h2{color:var(--ink);margin:0}
  .case-grid .body{font-size:15px;line-height:1.7;color:var(--dim);margin:22px 0 0;max-width:560px}
  .case-grid .src{font-size:10px;font-weight:500;color:var(--faint);margin-top:22px;letter-spacing:.14em;text-transform:uppercase}
  .dark-card{border:1px solid var(--line-strong);border-radius:2px;background:var(--raised)}
  .dark-card .r{display:flex;justify-content:space-between;padding:16px 18px;border-bottom:1px solid var(--line);font-size:14px}
  .dark-card .r:last-child{border-bottom:0}
  .dark-card .r span:first-child{color:var(--dim)}
  .dark-card .r span:last-child{font-family:"IBM Plex Mono",ui-monospace,monospace;font-weight:400;color:var(--ink)}

  /* tally band */
  .lockup{display:flex;align-items:center;gap:13px;margin-bottom:22px}
  .lockup .tile{width:44px;height:44px;background:var(--raised);border:1px solid var(--line-strong);border-radius:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
  .lockup .wm{display:block;font-weight:500;font-size:22px;letter-spacing:-.02em;line-height:1;color:var(--ink)}
  .lockup .by{display:block;font-size:9px;letter-spacing:.24em;color:var(--steel);margin-top:6px;text-transform:uppercase}
  .tally-grid{display:grid;grid-template-columns:1fr 420px;gap:64px;align-items:start;margin-top:18px}
  @media (max-width:960px){.tally-grid{grid-template-columns:1fr}}
  .bullets{margin-top:26px;display:grid;gap:12px;font-size:14px;color:var(--dim)}
  .bullets>div{display:flex;gap:13px}
  .bullets .sq{font-size:9px;color:var(--steel);padding-top:6px;line-height:1}
  .sample .r{display:flex;justify-content:space-between;padding:15px 18px;border-bottom:1px solid var(--line-soft);font-size:14px}
  .sample .r span:first-child{color:var(--dim)}
  .sample .r span:last-child{font-family:"IBM Plex Mono",ui-monospace,monospace;color:var(--ink)}
  .sample .foot{padding:13px 18px;border-top:1px solid var(--line);font-size:10px;font-weight:500;color:var(--faint);letter-spacing:.14em;text-transform:uppercase}

  /* packs */
  .packs{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px}
  @media (max-width:900px){.packs{grid-template-columns:1fr}}
  .pack{background:var(--card);border:1px solid var(--line);border-radius:2px;padding:24px;display:flex;flex-direction:column;transition:border-color .15s ease}
  .pack:hover{border-color:var(--line-strong)}
  .pack-head{display:flex;justify-content:space-between;align-items:baseline;gap:10px}
  .pk-trigger{font-size:10px;font-weight:500;letter-spacing:.14em;color:var(--steel);text-transform:uppercase}
  .pk-price{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;color:var(--dim)}
  .pk-price b{font-size:20px;color:var(--ink);font-weight:500}
  .pack h3{font-size:18px;font-weight:500;margin:14px 0 10px;color:var(--ink);letter-spacing:-.015em}
  .pack p{font-size:14px;line-height:1.65;color:var(--dim);margin:0;flex:1}
  .pk-foot{margin-top:18px;padding-top:14px;border-top:1px solid var(--line-soft);font-size:9.5px;letter-spacing:.14em;color:var(--faint);text-transform:uppercase}
  .suite{margin-top:16px;background:var(--card);border:1px solid var(--line);border-radius:2px;padding:22px 24px;display:flex;justify-content:space-between;align-items:center;gap:20px;flex-wrap:wrap}
  .suite h3{font-size:17px;font-weight:500;color:var(--ink);letter-spacing:-.015em}
  .trig-cat{margin-top:30px}
  .trig-grid{display:grid;grid-template-columns:1fr 1fr 1fr;border:1px solid var(--line);border-top:0;border-radius:0 0 2px 2px;background:var(--card)}
  @media (max-width:820px){.trig-grid{grid-template-columns:1fr}}
  .trig-grid>div{padding:15px 18px;border-bottom:1px solid var(--line-soft);border-right:1px solid var(--line-soft)}
  .trig-grid>div b{display:block;font-size:10px;letter-spacing:.14em;color:var(--steel);font-weight:500;text-transform:uppercase}
  .trig-grid>div span{display:block;margin-top:6px;font-size:13px;color:var(--dim)}

  /* faq */
  .faq-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 64px;margin-top:30px;border-top:1px solid var(--line)}
  .faq-grid>div,.faq-grid>details{padding:24px 0;border-bottom:1px solid var(--line)}
  .faq-grid h3{font-size:15px;font-weight:500;margin:0 0 10px;color:var(--ink)}
  .faq-grid p{font-size:14px;line-height:1.65;color:var(--dim);margin:0}
  @media (max-width:820px){.faq-grid{grid-template-columns:1fr}}
  /* The questions are details elements: a phone gets an accordion — nine
     open answers were most of a ten-thousand-pixel page — and a desktop
     gets the open grid it always had, the summaries inert so the answers
     cannot be folded away where there is room to show them. JS keeps the
     open attribute in step with the breakpoint. */
  /* Ten open answers turned out to be a wall on a desktop too — the fold
     is the behaviour at every width now, with the affordances a pointer
     expects: the question warms on hover and the plus turns to a minus. */
  .faq-grid summary{list-style:none}
  .faq-grid summary::-webkit-details-marker{display:none}
  .faq-grid summary h3{margin:0;transition:color .15s ease}
  .faq-grid summary:hover h3{color:var(--steel)}
  .faq-grid details p:first-of-type{margin-top:10px}
  .faq-grid>details{padding:0}
  .faq-grid summary{cursor:pointer;position:relative;display:flex;align-items:center;
    min-height:44px;padding:14px 34px 14px 0}
  .faq-grid summary::after{content:"+";position:absolute;right:4px;top:50%;
    transform:translateY(-50%);font-size:20px;font-weight:300;color:var(--steel-dim);
    transition:transform .25s cubic-bezier(.22,1,.36,1)}
  .faq-grid details[open] summary::after{content:"\2013";transform:translateY(-50%) rotate(180deg)}
  .faq-grid details p:last-of-type{padding-bottom:18px}

  /* contact */
  .contact-band{background:var(--card)}
  .contact-grid{display:grid;grid-template-columns:1fr 460px;gap:64px;align-items:start}
  @media (max-width:960px){.contact-grid{grid-template-columns:1fr}}
  .form-card{border:1px solid var(--line-strong);border-radius:2px;background:var(--raised)}
  .form-body{padding:20px;display:grid;gap:15px}
  .lbl{display:block;font-size:9.5px;font-weight:500;letter-spacing:.14em;color:var(--faint);margin-bottom:8px;text-transform:uppercase}
  .fld{width:100%;height:42px;border:1px solid var(--line-strong);border-radius:2px;padding:0 13px;font:400 14px/1.5 "Switzer",system-ui,sans-serif;color:var(--ink);background:#0D1217}
  textarea.fld{height:auto;min-height:78px;padding:11px 13px;resize:vertical}
  .fld::placeholder{color:#4E545D}
  .fld:focus{outline:none;border-color:var(--steel)}
  .frow{display:grid;gap:15px;grid-template-columns:1fr 1fr}
  @media (max-width:640px){.frow{grid-template-columns:1fr}}
  .fstep{display:none}
  .fstep.on{display:grid;gap:15px}
  .chiprow{display:flex;gap:8px;flex-wrap:wrap}
  .pick{border:1px solid var(--line-strong);background:transparent;border-radius:2px;padding:10px 16px;font:500 13px "Switzer",system-ui,sans-serif;color:var(--dim);cursor:pointer;transition:all .15s ease}
  .pick:hover{border-color:var(--steel);color:var(--steel)}
  .pick.on{background:var(--steel);border-color:var(--steel);color:#FFFFFF}
  .wizfoot{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:4px;flex-wrap:wrap}
  .wizfoot .note{font-size:9.5px;font-weight:500;color:var(--faint);letter-spacing:.14em;text-transform:uppercase}
  .btn-sm{height:40px;padding:0 20px;font-size:11.5px}

  /* ── what we do ───────────────────────────────────────────────────────
     Rows, not a table. A table asks you to read across three columns; a row
     with the title set large asks you to read one thing. The tile field
     behind gives the dark ground something to do without motion. */
  .ways{position:relative;overflow:hidden}
  /* one warm bloom off the top right, the way the atrium is lit */
  .glow{position:absolute;top:-140px;right:-160px;width:520px;height:520px;
    border-radius:50%;pointer-events:none;z-index:0;
    background:radial-gradient(circle,rgba(107,139,164,.10),transparent 68%);
    filter:blur(30px)}
  .ways-in{position:relative;z-index:1}
  .ways-head{display:grid;grid-template-columns:1fr auto;gap:40px;align-items:end;
    padding-bottom:34px;border-bottom:1px solid var(--line)}
  .ways-head h2{font-size:clamp(34px,5.6vw,62px);line-height:1.04;letter-spacing:-.032em;
    font-weight:500;margin:14px 0 0}
  .ways-lede{font-size:15px;color:var(--dim);max-width:26ch;text-align:right;margin:0}

  /* ── row hover ────────────────────────────────────────────────────────
     The brand's one piece of motion is light travelling along an edge —
     it is what the atrium plate is actually a photograph of. So a row does
     not light up as a block; a line runs along its base, a warm wash bleeds
     in from the left, and the row steps forward a little. */
  .way{position:relative;display:grid;
    grid-template-columns:52px minmax(150px,1fr) minmax(0,1.5fr) 52px;
    gap:26px;align-items:center;padding:30px 0;border-bottom:1px solid var(--line);
    color:inherit;isolation:isolate;
    transition:padding-left .32s cubic-bezier(.22,.61,.36,1)}
  /* the wash */
  .way::before{content:"";position:absolute;inset:-1px 0;z-index:-1;pointer-events:none;
    background:linear-gradient(90deg,rgba(107,139,164,.09),rgba(107,139,164,.02) 46%,transparent 72%);
    opacity:0;transition:opacity .3s ease}
  /* the travelling edge */
  .way::after{content:"";position:absolute;left:0;bottom:-1px;height:1px;width:100%;
    background:linear-gradient(90deg,var(--steel),rgba(107,139,164,.35) 62%,transparent);
    transform:scaleX(0);transform-origin:left;
    transition:transform .5s cubic-bezier(.22,.61,.36,1)}
  .way:hover,.way:focus-visible{padding-left:14px}
  .way:hover::before,.way:focus-visible::before{opacity:1}
  .way:hover::after,.way:focus-visible::after{transform:scaleX(1)}

  .way .n{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;color:var(--faint);
    transition:color .25s ease}
  .way:hover .n{color:var(--steel)}
  .way .ttl{font-size:clamp(26px,3.4vw,40px);line-height:1.05;letter-spacing:-.028em;
    font-weight:500;color:var(--ink)}
  .way .desc{font-size:15px;line-height:1.6;color:var(--dim);transition:color .25s ease}
  .way:hover .desc{color:var(--ink)}
  .way .arw{width:44px;height:44px;border-radius:50%;display:grid;place-items:center;
    border:1px solid var(--line-strong);background:transparent;color:var(--steel);
    font-size:17px;justify-self:end;
    transition:background .25s ease,color .25s ease,border-color .25s ease,transform .3s cubic-bezier(.22,.61,.36,1)}
  .way:hover .arw{background:var(--steel);border-color:var(--steel);color:#FFFFFF;
    transform:translate(3px,-3px)}
  @media (prefers-reduced-motion:reduce){
    .way,.way::before,.way::after,.way .arw{transition:none}
    .way:hover{padding-left:0}
    .way:hover::after{transform:scaleX(1)}
  }
  @media (max-width:820px){
    .ways-head{grid-template-columns:1fr;gap:16px;align-items:start}
    .ways-lede{text-align:left;max-width:none}
    .way{grid-template-columns:38px 1fr 44px;gap:8px 16px;padding:24px 0}
    .way .ttl{grid-column:2}
    .way .desc{grid-column:2;margin-top:8px}
    .way .arw{grid-row:1;grid-column:3}
  }
  @media (prefers-reduced-motion:reduce){.tiles i{animation:none;opacity:.03}}

  /* ── approach ─────────────────────────────────────────────────────────
     A centred statement, then a spine. The rail and the marks are what make
     three cards read as one sequence instead of three more boxes; without
     it this section is the same shape as every other section on the page. */
  .approach .ap-head{text-align:center;max-width:760px;margin:0 auto 62px}
  .chip{display:inline-block;font-size:11px;font-weight:600;letter-spacing:.16em;
    text-transform:uppercase;color:#FFFFFF;background:var(--steel);padding:4px 9px;border-radius:2px}
  .approach h2{font-size:clamp(32px,5vw,54px);line-height:1.06;letter-spacing:-.03em;
    font-weight:500;margin:20px 0 0}
  .approach h2 em{font-style:italic;font-weight:400;color:var(--steel);
    font-family:Georgia,"Times New Roman",serif;letter-spacing:-.01em}
  .approach .ap-head p{font-size:16px;color:var(--dim);margin:16px 0 0}

  .steps{list-style:none;margin:0 auto;padding:0;max-width:780px;counter-reset:none}
  .step{position:relative;padding-left:66px;padding-bottom:34px}
  .step:last-child{padding-bottom:0}
  /* One spine for the whole list rather than a segment per step. The unlit
     track runs the full height; a second element fills it as you scroll, so
     the line travels the section instead of three lines appearing in turn. */
  .steps{position:relative}
  .steps::before{content:"";position:absolute;left:21px;top:22px;bottom:22px;width:1px;
    background:var(--line-strong)}
  .spine{position:absolute;left:21px;top:22px;width:1px;height:0;
    background:linear-gradient(180deg,rgba(107,139,164,.35),var(--steel));
    box-shadow:0 0 10px rgba(107,139,164,.45)}
  .spine::after{content:"";position:absolute;left:-2px;bottom:-3px;width:5px;height:5px;
    border-radius:50%;background:var(--steel);box-shadow:0 0 12px 2px rgba(107,139,164,.7)}
  .step .dot{position:absolute;left:0;top:0;width:44px;height:44px;border-radius:50%;
    display:grid;place-items:center;background:var(--raised);
    border:1px solid var(--line-strong);color:var(--steel)}
  .step .dot svg{width:20px;height:20px;transition:color .35s ease}
  .step .dot{transition:border-color .35s ease,background .35s ease,box-shadow .35s ease}
  .step.lit .dot{border-color:var(--steel);background:#141B2B;
    box-shadow:0 0 0 4px rgba(107,139,164,.09)}
  .step.lit .dot svg{color:var(--steel-hi)}
  .step .card{border:1px solid var(--line);border-radius:4px;background:var(--card);padding:22px 24px}
  .step .n{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
    letter-spacing:.14em;color:var(--faint)}
  .step h3{font-size:clamp(20px,2.6vw,27px);line-height:1.15;letter-spacing:-.022em;
    font-weight:500;color:var(--ink);margin:10px 0 0}
  .step .card p{font-size:15px;line-height:1.65;color:var(--dim);margin:12px 0 0}
  @media (max-width:640px){
    .approach .ap-head{margin-bottom:40px}
    .step{padding-left:52px;padding-bottom:26px}
    .step .dot{width:38px;height:38px}
    .steps::before,.spine{left:18px}
    .step .card{padding:18px}
  }

  /* footer */
  footer{border-top:1px solid var(--line)}
  .foot{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px;padding:32px}
  .foot .legal,.foot .links{font-size:10px;font-weight:500;letter-spacing:.14em;color:var(--faint);text-transform:uppercase}
  .foot .links{display:flex;gap:22px}
  .foot .links a{color:var(--dim)}
  .foot .links a:hover{color:var(--steel)}
  /* tan sign-off bar, as in the brand guide */
  .signoff{background:var(--steel);color:#FFFFFF}
  .signoff-in{display:flex;align-items:center;gap:18px;padding:16px 32px;flex-wrap:wrap}
  .signoff .name{font-size:12px;font-weight:500;letter-spacing:.22em;text-transform:uppercase}
  .signoff .bar{width:1px;height:16px;background:rgba(244,246,248,.7)}
  .signoff .tagline{font-size:11px;font-weight:400;letter-spacing:.18em;text-transform:uppercase;color:rgba(255,255,255,0.82)}
  @media (max-width:640px){.signoff-in{padding:16px 20px;gap:12px}.signoff .bar{display:none}}

/* ── sub-page hero ────────────────────────────────────────────────────
   Tally and Contact open on words alone. The atrium plate belongs to the
   home page — one photograph on the site, not one per page. */
/* padding-block only — the shorthand was resetting .wrap's side
   gutters to 0 and pinning the text to the edge of the screen */
.subhero{padding-top:150px;padding-bottom:56px;border-bottom:1px solid var(--line)}
.subhero .eyebrow{margin-bottom:22px}
.subhero h1.setwide{margin-top:0}
/* the claim above the name of the page — set at reading size, not statement
   size, because the statement here is "Practitioners. Not a panel." and two
   competing sizes above one headline is two headlines */
.subhero-claim{font-size:clamp(18px,1.9vw,24px); line-height:1.4; letter-spacing:-.015em;
  color:var(--dim); max-width:34ch; margin:clamp(14px,1.6vw,20px) 0 0}

.subhero .sub{font-size:17px;line-height:1.65;color:var(--dim);margin:24px 0 0;max-width:620px}
.subhero .hero-ctas{display:flex;align-items:center;gap:24px;margin-top:34px;flex-wrap:wrap}
@media (max-width:640px){
  .subhero{padding-top:104px;padding-bottom:40px}
  .subhero h1.setwide{font-size:29px;letter-spacing:.012em;line-height:1.2}
  .subhero .sub{font-size:15.5px;margin-top:18px}
  .subhero .hero-ctas{display:grid;grid-template-columns:1fr;gap:0}
  .subhero .hero-ctas .btn{width:100%;height:54px;justify-content:center}
  .subhero .hero-ctas .ulink{justify-self:center;margin-top:14px;min-height:44px;
    display:inline-flex;align-items:center;border-bottom:0;font-size:13px;color:var(--dim)}
}

/* ── closing band ─────────────────────────────────────────────────────
   The contact form lives on its own page now, so the home page needs an
   ending rather than simply stopping after the FAQ. */
.closer{background:var(--card);border-top:1px solid var(--line)}
.closer h2{font-size:clamp(30px,4.4vw,46px);line-height:1.08;letter-spacing:-.03em;
  font-weight:500;margin:16px 0 0}
.closer p{font-size:16px;line-height:1.65;color:var(--dim);margin:16px 0 0;max-width:56ch}
.closer .hero-ctas{display:flex;align-items:center;gap:24px;margin-top:30px;flex-wrap:wrap}
@media (max-width:640px){
  .closer .hero-ctas{display:grid;grid-template-columns:1fr;gap:0}
  .closer .hero-ctas .btn{width:100%;height:54px;justify-content:center}
  .closer .hero-ctas .ulink{justify-self:center;margin-top:14px;min-height:44px;
    display:inline-flex;align-items:center;border-bottom:0;font-size:13px;color:var(--dim)}
}

/* the number slot carries the range under it — a service and what it costs
   are one fact, so they share a column rather than becoming a price list */
.way .n{display:flex;flex-direction:column;gap:5px}
.way .n i{font-style:normal;font-size:11px;letter-spacing:.02em;color:var(--faint);white-space:nowrap}
.way:hover .n i{color:var(--steel)}
@media (min-width:821px){.way{grid-template-columns:132px minmax(150px,1fr) minmax(0,1.35fr) 52px}}

/* ── hero breaks the measure ──────────────────────────────────────────
   Everything else on the site sits in the 1160px column. The hero does
   not: the plate is full-bleed, so type centred in a narrow column reads
   as a box floating on a photograph. It runs to the edges instead, and
   the facets go with it to the far side. */
.hero-zone .hero{max-width:none;
  padding-left:clamp(22px,4.4vw,88px);padding-right:clamp(22px,4.4vw,88px)}
@media (min-width:1001px){
  .hero-zone .hero{grid-template-columns:minmax(0,1fr) 300px;gap:clamp(40px,6vw,110px)}
}

/* No panel behind the facets. In the reference they sit straight on the
   picture, and the plate is dark enough there to carry them — the scrim
   was solving a problem that the wider gutters moved out of the way. */
.hero-facets{background:none;-webkit-backdrop-filter:none;backdrop-filter:none;
  padding:0;margin:0}
.facet b{font-size:11px}
.facet span{font-size:12.5px;line-height:1.45}

/* The bar sits above a full-bleed hero, so it has to share the hero's
   gutters — held inside the 1160px column it reads as a separate object
   floating over the picture rather than the top edge of the page. */
.nav-in{max-width:none;padding-left:clamp(22px,4.4vw,88px);padding-right:clamp(22px,4.4vw,88px)}

/* The facets sit over the colonnade, which is the lit part of the plate.
   A panel behind them reads as a pasted box, so the frame falls off at the
   right edge instead — it looks like the light running out, which is what
   the picture is already doing everywhere else — and the type carries a
   shadow so it never depends on whichever pixel it lands on. */
@media (min-width:1001px){
  .hero-zone .veil{background:
    linear-gradient(90deg,rgba(255,255,255,0.49) 0%,rgba(255,255,255,0.19) 34%,transparent 58%,
      rgba(255,255,255,0.36) 74%,rgba(255,255,255,0.82) 100%)}
}
.facet b,.facet span{text-shadow:0 1px 10px rgba(255,255,255,.95),0 0 26px rgba(244,246,248,.92)}
.facet{border-top-color:rgba(107,139,164,.28)}

/* The rule over the pillars used to be a border on a block inside the
   1160px column. Now that the hero runs to the edges it stretched with
   it, straight across the picture. Short mark instead, set over the words
   it belongs to — which is what the reference had. */
.hero .pillars{border-top:0;position:relative;padding-top:28px}
.hero .pillars::before{content:"";position:absolute;top:0;left:0;width:54px;height:1px;
  background:var(--steel-dim)}

/* ── hero entrance ────────────────────────────────────────────────────
   The page opens the way the picture reads: one thing at a time, from
   the ground up. Lines rise and settle in sequence, then a single pass
   of light crosses the tan line — the same gesture as the row hover and
   as the plate itself. Transform and opacity only, so nothing here costs
   a layout. Held off entirely for anyone who asked for less motion. */
@media (prefers-reduced-motion:no-preference){
  .hero h1.setwide span,.hero .sub,.hero-ctas,
  .hero-note,.hero .pillars,.hero-facets .facet{
    opacity:0;animation:riseIn .8s cubic-bezier(.16,.84,.44,1) forwards}
  @keyframes riseIn{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:none}}

  .hero h1.setwide span:nth-child(1){animation-delay:.22s}
  .hero h1.setwide span:nth-child(2){animation-delay:.32s}
  .hero h1.setwide span:nth-child(3){animation-delay:.42s}
  .hero .sub                {animation-delay:.56s}
  .hero-ctas                {animation-delay:.66s}
  .hero-note                {animation-delay:.74s}
  .hero .pillars            {animation-delay:.82s}
  .hero-facets .facet:nth-child(1){animation-delay:.72s}
  .hero-facets .facet:nth-child(2){animation-delay:.80s}
  .hero-facets .facet:nth-child(3){animation-delay:.88s}

  /* the short mark over the pillars draws itself rather than appearing */
  .hero .pillars::before{transform-origin:left;animation:drawRule .7s cubic-bezier(.16,.84,.44,1) .92s both}
  @keyframes drawRule{from{transform:scaleX(0)}to{transform:scaleX(1)}}

  /* A shine used to run left to right across the accent word 1.05s after
     load. That is the animation Dixon kept reporting — five times, while I
     looked for it in the JavaScript. It was here, in CSS, and it is gone. */
}

/* ── problem picker ───────────────────────────────────────────────────
   A visitor who cannot name the service they want can always name the
   thing that annoys them. So the six services are stated as six
   complaints, and picking one is the route in.

   The reference floats a white card on a photograph. A white card here
   would drop a hole in the middle of a dark site, so the card is raised
   dark on a hairline instead — same gesture, this palette. */
.picker .pick-head{display:grid;grid-template-columns:1fr auto;gap:40px;align-items:end;
  margin-bottom:38px}
.picker h2{font-size:clamp(30px,4.6vw,50px);line-height:1.06;letter-spacing:-.03em;
  font-weight:500;margin:16px 0 0;max-width:20ch}
.picker h2 em{font-style:italic;font-weight:400;color:var(--steel);
  font-family:Georgia,"Times New Roman",serif;letter-spacing:-.01em}
.picker .pick-head p{font-size:16px;line-height:1.6;color:var(--dim);margin:16px 0 0;max-width:46ch}

.pick-stage{position:relative;border-radius:6px;overflow:hidden;
  padding:clamp(24px,4vw,64px);border:1px solid var(--line)}
.pick-plate{position:absolute;inset:0;z-index:0}
.pick-plate img{width:100%;height:100%;object-fit:cover;object-position:60% center;display:block}
.pick-stage::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgba(255,255,255,0.53),rgba(255,255,255,0.71))}

.pick-card{position:relative;z-index:2;max-width:820px;margin:0 auto;
  background:rgba(255,255,255,.9);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  border:1px solid var(--line-strong);border-radius:5px;padding:clamp(20px,2.6vw,30px)}
.pick-card-head{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;
  padding-bottom:20px;border-bottom:1px solid var(--line)}
.pick-card .k{font-size:12px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.pick-card h3{font-size:clamp(19px,2.2vw,25px);line-height:1.16;letter-spacing:-.022em;
  font-weight:500;color:var(--ink);margin:8px 0 0}
.pill{flex-shrink:0;font-size:11.5px;font-weight:500;letter-spacing:.08em;color:var(--steel);
  border:1px solid rgba(107,139,164,.4);border-radius:999px;padding:6px 13px;white-space:nowrap}

.pick-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:20px}
.opt{display:grid;grid-template-columns:1fr auto;align-items:center;gap:4px 14px;
  min-height:66px;padding:14px 16px;border:1px solid var(--line);border-radius:4px;
  background:rgba(244,246,248,.85);color:inherit;
  transition:border-color .22s ease,background .22s ease,transform .22s cubic-bezier(.22,.61,.36,1)}
.opt b{font-size:14.5px;font-weight:600;letter-spacing:-.005em;color:var(--ink)}
.opt span{grid-column:1;font-size:12.5px;line-height:1.45;color:var(--dim)}
.opt i{grid-row:1/3;font-style:normal;font-size:15px;color:var(--faint);transition:color .22s ease,transform .22s ease}
.opt:hover{border-color:var(--steel);background:rgba(107,139,164,.06);transform:translateX(3px)}
.opt:hover i{color:var(--steel);transform:translateX(2px)}

.pick-foot{margin-top:16px;padding:14px 16px;border:1px solid var(--line);border-radius:4px;
  font-size:13.5px;line-height:1.55;color:var(--dim);background:rgba(244,246,248,.7)}

@media (max-width:820px){
  .picker .pick-head{grid-template-columns:1fr;gap:22px;align-items:start}
  .picker .pick-head .btn{justify-self:start}
  .pick-grid{grid-template-columns:1fr}
  .pick-card-head{flex-direction:column;gap:12px}
  .pick-stage{padding:16px;border-radius:4px}
}

/* ── scroll reveal ────────────────────────────────────────────────────
   One mechanism for the whole page: an element is marked, it starts low
   and clear, and it settles when it comes into view. Groups stagger by
   position so a list arrives as a list rather than all at once.
   Everything is opt-in through .reveal, so nothing moves for a visitor
   who asked for less motion — the class is never applied. */
.reveal{opacity:0;transform:translateY(22px);
  transition:opacity .7s cubic-bezier(.16,.84,.44,1),transform .7s cubic-bezier(.16,.84,.44,1)}
.reveal.in{opacity:1;transform:none}

/* the spine is driven by scroll position, not by a per-step transition */
/* the mark pops a beat after its card */
.step .dot{transform:scale(.7);opacity:0;
  transition:transform .55s cubic-bezier(.34,1.4,.5,1) .12s,opacity .4s ease .12s}
.step.in .dot{transform:none;opacity:1}

/* the arrow was landing in the first column and shunting the label to the
   right — it belongs in the second, spanning both rows */
.opt{grid-template-columns:1fr 20px}
.opt b{grid-column:1;grid-row:1;justify-self:start;text-align:left}
.opt span{grid-column:1;grid-row:2;text-align:left}
.opt i{grid-column:2;grid-row:1/3;justify-self:end}
/* and the plate was cropped onto its own dark edge, so the rays never showed */
.pick-plate img{object-position:38% center}

/* The card was held to 820px inside a stage twice that wide, so the panel
   floated small in a large frame. It fills the stage now — the plate's job
   here is to frame it, not to sit around it. */
.pick-card{max-width:none}
.pick-grid{gap:14px}
.opt{min-height:74px;padding:16px 18px}
.opt b{font-size:15.5px}
.opt span{font-size:13px}

/* ── who we are ───────────────────────────────────────────────────────
   A consultancy sells a person, so this page puts one on the screen and
   then says four things about how the work is done. */
.who-lead{display:grid;grid-template-columns:280px 1fr;gap:clamp(28px,4vw,56px);align-items:start}
.portrait{aspect-ratio:4/5;border:1px solid var(--line-strong);border-radius:5px;
  background:linear-gradient(150deg,#171C24 20%,#2A303A 58%,#0E1218 92%);
  display:grid;place-items:center}
.portrait .ph{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  letter-spacing:.2em;color:var(--faint)}
.who .k{font-size:12px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--steel)}
.who h2{font-size:clamp(28px,4vw,42px);line-height:1.06;letter-spacing:-.03em;font-weight:500;margin:12px 0 0}
.who .role{font-size:15px;color:var(--dim);margin:10px 0 0}
.who p{font-size:16px;line-height:1.68;color:var(--dim);margin:16px 0 0;max-width:60ch}
.who-grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:clamp(38px,5vw,64px)}
.wcard{border:1px solid var(--line);border-radius:4px;background:var(--card);padding:24px}
.wcard b{display:block;font-size:17px;font-weight:600;letter-spacing:-.012em;color:var(--ink)}
.wcard p{font-size:14.5px;line-height:1.6;color:var(--dim);margin:12px 0 0;max-width:none}
@media (max-width:820px){
  .who-lead{grid-template-columns:1fr;gap:24px}
  .portrait{max-width:220px}
  .who-grid{grid-template-columns:1fr;gap:12px}
  .wcard{padding:18px}
}

/* The set-wide treatment was scoped under .hero, so the sub-page heroes on
   Tally, Contact and Who we are fell back to the default h1 and ran their
   lines together. It belongs to the class, not to the home page. */
h1.setwide{font-family:"Jost",system-ui,sans-serif;font-weight:300;
  text-transform:uppercase;letter-spacing:.055em;line-height:1.13;
  font-size:clamp(29px,5.1vw,62px);max-width:none}
h1.setwide span{display:block}
h1.setwide .accent{color:var(--steel)}
@media (max-width:640px){
  h1.setwide{font-size:29px;letter-spacing:.012em;line-height:1.2}
}

/* ── phone: reach and legibility ──────────────────────────────────────
   Everything a thumb has to hit gets 44px, and nothing sets below 12px.
   These were measured, not guessed: brand 35, nav contact 34, burger and
   sheet close 38; the hero note at 10.5, section eyebrows at 11, the
   footer at 10. */
@media (max-width:820px){
  nav .brand{min-height:44px}
  .nav-cta,.nav-signin{height:44px}
  .nav-burger,.sheet-close{width:44px;height:44px}
  .foot .links a{display:inline-flex;align-items:center;justify-content:center;min-height:44px;min-width:44px}

  .eyebrow{font-size:12px;letter-spacing:.1em}
  .hero-note{font-size:12px;letter-spacing:.08em}
  .foot .legal,.foot .links{font-size:12px;letter-spacing:.08em}
  .picker .pick-card .k,.approach .chip,.ways .eyebrow{font-size:12px}
  .pill{font-size:12px}
  .opt span{font-size:13px}
  .way .n{font-size:12px}
}

/* the secondary link was drifting a long way under the button and
   centring itself, which read as a second, competing call */
@media (max-width:640px){
  .hero-ctas .ulink,.subhero .hero-ctas .ulink,.closer .hero-ctas .ulink{
    justify-self:start;margin-top:8px}
}

/* the footer carries a second lockup, which the nav-scoped rule missed */
@media (max-width:820px){
  .foot .brand{min-height:44px}
  .signoff .tagline,.signoff .name{font-size:12px}
}

/* ── phone: the sections that came over from the old single page ──────
   The flows and packs blocks were built for a desktop schematic and had
   never been sized for a thumb: figure tabs at 41px, the small buttons
   at 40, and mono labels running down to 9px. */
@media (max-width:820px){
  .btn-sm{height:46px;padding:0 22px;font-size:13px}
  .ulink{min-height:44px;display:inline-flex;align-items:center}
  .fig-tab{min-height:48px;font-size:12px;letter-spacing:.06em}

  .tally-card .by,.rcard .by{font-size:12px}
  .bp-head .fig,.bp-head .note{font-size:12px;letter-spacing:.06em}
  .node .k{font-size:12px;letter-spacing:.06em}
  .node b{font-size:14px}
  .node .d{font-size:12.5px}
  .loop{font-size:12px}
  .bp-out .lead,.bp-out .chips{font-size:12px}
  .spec .cols,.spec .tag{font-size:12px}
  .pack .k,.pack .fig{font-size:12px}
  .case-grid .src{font-size:12px;letter-spacing:.08em}
  .wizfoot .note,.lbl{font-size:12px;letter-spacing:.08em}
}

/* the last of them, found by walking the rendered page rather than by
   guessing at class names */
@media (max-width:820px){
  .rcard .by,.rcard-head span,.rcard.sample .foot{font-size:12px;letter-spacing:.06em}
  .pack-head .pk-trigger,.pk-trigger,.pack .pk-foot{font-size:12px;letter-spacing:.06em}
}

@media (max-width:820px){
  /* these have to match the specificity of the desktop rules they override
     — .lockup .by and .trig-grid>div b both outrank a bare class */
  .lockup .by{font-size:12px;letter-spacing:.14em}
  .trig-grid>div b{font-size:12px;letter-spacing:.08em}
  .trig-grid>div span{font-size:13px}
}

/* ── phone: the enquiry form ──────────────────────────────────────────
   Fields at 42px and a bare email link at 18px. A form is the one place
   on the site where a missed tap costs an enquiry. */
@media (max-width:820px){
  .fld{height:48px;font-size:16px}          /* 16px also stops iOS zooming on focus */
  select.fld{height:48px}
  textarea.fld{min-height:96px;font-size:16px}
  .pick{min-height:46px}
  .contact-band a[href^="mailto:"]{display:inline-flex;align-items:center;min-height:44px}
}

/* The bar floats on the plate until it docks, and this plate puts its
   brightest run of light exactly where the sign-in button sits. A short
   fall-off from the top edge carries the bar without reading as a filled
   header — it only exists while the nav is undocked. */
nav:not(.docked)::before{content:"";position:absolute;inset:0 0 auto 0;height:132px;
  z-index:-1;pointer-events:none;
  background:linear-gradient(180deg,rgba(255,255,255,0.88),rgba(255,255,255,0.49) 46%,transparent)}
nav{isolation:isolate}
.nav-signin{backdrop-filter:none}
nav:not(.docked) .nav-signin{border-color:rgba(107,139,164,.45);color:var(--ink) !important}

/* ── picker at the hero's width ───────────────────────────────────────
   The stage was sitting inside the 1160px column while the hero ran to
   the edges, so the one interactive block on the page read smaller than
   the picture above it. It takes the same gutters now, and the type
   inside grows with it — the options are the point of the section, not
   a footnote to it. */
@media (min-width:821px){
  .picker .sec{max-width:none;
    padding-left:clamp(22px,4.4vw,88px);padding-right:clamp(22px,4.4vw,88px)}
  .pick-stage{padding:clamp(34px,4.4vw,76px)}
  .pick-card{padding:clamp(28px,3vw,40px)}
  .pick-card h3{font-size:clamp(24px,2.9vw,34px)}
  .pick-grid{gap:16px}
  .opt{min-height:92px;padding:20px 24px}
  .opt b{font-size:17.5px}
  .opt span{font-size:14px}
  .opt i{font-size:18px}
  .pick-foot{font-size:14.5px;padding:18px 20px}
  .pick-card .k{font-size:12.5px}
}

/* The card was told to fill the stage, which left the plate as a sliver
   down one edge. Reversed: the stage keeps the hero's width, and the card
   sits back inside it so the rays read as a picture the panel is placed
   on rather than as a border around it. */
@media (min-width:821px){
  .pick-stage{padding:clamp(56px,7vw,120px) clamp(40px,7vw,130px)}
  .pick-card{max-width:900px;margin:0 auto}
  .pick-plate img{object-position:46% center}
  /* less scrim, since the plate is now something to look at */
  .pick-stage::after{background:linear-gradient(180deg,rgba(255,255,255,0.36),rgba(255,255,255,0.58))}
}

/* the option rows were pushed up with the card and overshot — they only
   have to hold two short lines */
@media (min-width:821px){
  .opt{min-height:0;padding:14px 18px}
  .opt b{font-size:15.5px}
  .opt span{font-size:13px}
  .opt i{font-size:16px}
  .pick-grid{gap:12px}
  .pick-foot{font-size:13.5px;padding:14px 18px;margin-top:14px}
}

/* This plate carries all of its light in the right half, so a centred crop
   leaves the strip to the left of the card as flat black and the section
   looks lit from one side only. Pushing the window right puts rays on both
   sides of the panel. */
@media (min-width:821px){
  .pick-plate img{object-position:66% center}
  .pick-stage::after{background:linear-gradient(180deg,rgba(255,255,255,0.28),rgba(255,255,255,0.52))}
}

/* On a phone the stage has 16px of padding, so the plate shows as a hairline
   down one edge and nothing else — 96KB of image for something nobody can
   see, on the connection least able to afford it. The card stands on the
   paper instead. */
@media (max-width:820px){
  .pick-plate{display:none}
  .pick-stage{border:0;padding:0;background:none}
  .pick-stage::after{display:none}
  .pick-card{border-radius:5px}
}

/* ── hero, closer to the mockup ───────────────────────────────────────
   The reference has no eyebrow, no note line, and a facet column set
   much quieter than the one that got built. Its restraint is the whole
   effect, so those come off. The call to action stays — a hero without
   one is a picture — but everything around it gets out of its way.

   One exception, added back deliberately: the eyebrow. The headline is
   three pronouns — Find it, Build it, Run it — and nothing else above
   the fold names what "it" is. The reference could afford that because
   its reader already knew the company. A visitor who has never heard of
   DDX cannot tell this from a recruiter or a builder in the first three
   seconds. The eyebrow is the shortest sentence that fixes it, so it is
   the one piece of restraint being spent. It carries the short line, not
   the long one: at 11px in caps, forty characters is a paragraph. */
@media (min-width:821px){
  .hero h1.setwide{margin-top:0}
  .hero .sub{max-width:36ch;font-size:16px;line-height:1.7;margin-top:24px}
  .hero .pillars{margin-top:40px;padding-top:26px}

  /* the facets in the reference are small marks, not cards */
  .hero-facets{gap:22px;max-width:250px}
  .facet{grid-template-columns:22px 1fr;column-gap:12px;padding-top:11px}
  .fmark{width:16px;height:16px}
  .facet b{font-size:10.5px;letter-spacing:.14em}
  .facet span{font-size:12px;line-height:1.42;margin-top:5px}
}

/* Wherever the hero eyebrow shows at all, it shows the short line. The long
   one runs to seventy-eight characters, and at 11px in caps with .18em
   tracking that is a rule of text across the whole column — the opposite of
   a line you can read in three seconds. */

/* the pillars are separated by small dots in the reference, not diamonds */
.pillars span::before{width:3px;height:3px;border-radius:50%;transform:none;
  background:var(--steel-dim);margin-right:11px}

/* ── phone: let the picture reach the edges ───────────────────────────
   A band with side gutters, a border and a radius reads as a card
   dropped into the page. Full bleed reads as the hero it is. */
@media (max-width:640px){
  .hero-index{margin:26px -20px 0}
  .hix-plate{height:300px;margin-right:0;border:0;border-radius:0;
    border-top:1px solid var(--line)}
  .hix-plate .cap{left:20px;bottom:18px}
}

/* narrowing the column's contents left it floating 50px short of the
   gutter every other element lines up on — narrow the track instead */
@media (min-width:1001px){
  .hero-zone .hero{grid-template-columns:minmax(0,1fr) 250px}
}

/* an earlier rule pulled the plate 20px right of the gutter and my override
   only zeroed that side, so it sat inset on the right. Bleed both. */
@media (max-width:640px){
  .hero-index{margin-left:-20px;margin-right:-20px}
  .hix-plate{margin-left:0;margin-right:0;width:auto}
}

/* Negative margins were not widening the block — its width still resolved
   from the parent's content box, so only the left edge moved. The viewport
   technique does not depend on that: pin the width to the viewport and pull
   it back by half the difference. */
@media (max-width:640px){
  .hero-index{margin-left:0;margin-right:0}
  .hix-plate{width:100vw;margin-left:calc(50% - 50vw);margin-right:calc(50% - 50vw)}
}

/* ── phone hero, tightened ────────────────────────────────────────────
   The eyebrow came off here because it was a two-digit index, a rule and
   a forty-character line squeezed into 350px. That was the right call for
   that construction and the wrong outcome: a phone was left with a
   headline and nothing naming the trade above it. It comes back stripped
   to the label alone — no index, no rule, no forty-character variant —
   which is one line of 11px caps and costs the screen almost nothing.

   The rest was dead space: a gap under the secondary link, then a short
   picture that stopped well before the fold, so the screen read as a
   text block and a photograph rather than as one composition. The copy
   closes up and the picture runs to the bottom of the first screen. */
@media (max-width:640px){
  .hero-zone{min-height:100svh}
  .hero{padding-top:74px;padding-bottom:0}
  .hero h1.setwide{margin-top:0}
  .hero .sub{margin-top:16px}
  .hero-ctas{margin-top:24px}
  .hero-ctas .ulink{margin-top:10px}
  .hero-index{margin-top:22px}
  /* sized so the picture ends on the fold rather than leaving a band of
     empty hero under it — the copy above it is a fixed height here */
  .hix-plate{height:min(452px, 53svh)}
}

/* ── phone: the action follows you ────────────────────────────────────
   Persistent contact, the way the reference keeps a chat button pinned.
   Off until the hero has left the screen, so it never sits under a
   button that is already visible, and never on desktop where the nav
   carries one the whole way down. */
.dock{display:none}
@media (max-width:820px){
  .dock{position:fixed;left:0;right:0;bottom:0;z-index:35;display:flex;
    align-items:center;gap:14px;
    padding:12px 20px calc(12px + env(safe-area-inset-bottom));
    background:rgba(255,255,255,0.98);-webkit-backdrop-filter:blur(14px);
    backdrop-filter:blur(14px);border-top:1px solid var(--line);
    transform:translateY(102%);transition:transform .32s cubic-bezier(.22,.61,.36,1)}
  .dock.up{transform:none}
  .dock-cta{flex:1;height:50px;display:flex;align-items:center;justify-content:center;
    background:var(--steel);color:#FFFFFF;border-radius:3px;
    font-size:13.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase}
  .dock-note{font-size:12px;letter-spacing:.06em;color:var(--faint);
    text-transform:uppercase;white-space:nowrap}
  /* so the footer is never sitting under it */
  body.docked-cta{padding-bottom:78px}
  /* and the menu sheet covers it rather than fighting it */
  .sheet[data-open] ~ .dock{transform:translateY(102%)}
}
@media (prefers-reduced-motion:reduce){.dock{transition:none}}

/* ── phone hero: words on the picture ─────────────────────────────────
   Earlier attempts put type over this plate and lost — but that was the
   old corridor, and the phone crop was taken from the lit two thirds.
   The crop now comes from the left of the frame, where the top third
   measures 3.8/255 mean luminance. White type sits on that without a
   scrim at all, and the light line still arrives at the bottom of the
   frame under the button. */
@media (max-width:640px){
  .hero-zone{min-height:100svh;align-items:flex-start;background:var(--paper)}
  .hero-zone .plate{display:block;position:absolute;inset:0}
  .hero-zone .plate img{object-position:center}
  /* only enough to settle the foot of the frame, nothing over the words */
  .hero-zone .veil{display:block;background:linear-gradient(180deg,
    transparent 0%,transparent 52%,rgba(255,255,255,0.41) 74%,rgba(255,255,255,0.82) 100%)}

  .hero{padding-top:96px;padding-bottom:34px;position:relative;z-index:2}
  /* the separate picture band is what this replaces */
  .hero-index{display:none}
  .hero .pillars{display:flex;margin-top:30px;padding-top:20px}
}

/* ── phone hero: copy at the foot ─────────────────────────────────────
   Dixon's call, made after seeing the alternative. It costs a scrim:
   the bottom third of this plate measures 39.6 mean / 229 max, and no
   crop of it — six horizontal offsets and five vertical windows — has a
   dark foot, because the light line lands on the floor and reflects.
   So the scrim is shaped to spend as little as possible: nothing at all
   across the top 38%, where the ceiling and the upper run of the light
   stay untouched, then down hard under the words. */
@media (max-width:640px){
  .hero-zone{align-items:flex-end}
  .hero{padding-top:0;padding-bottom:38px}
  .hero-zone .veil{background:linear-gradient(180deg,
    transparent 0%,transparent 38%,rgba(255,255,255,0.58) 56%,
    rgba(255,255,255,0.98) 74%,rgba(255,255,255,0.98) 100%)}
  /* pull the frame down so the light line and the vanishing point sit in
     the clean upper half; the scrim then only covers floor */
  .hero-zone .plate img{object-position:center 68%}
  .hero .pillars{margin-top:24px;padding-top:18px}
}

/* The copy sat at 53% of the frame, which is fine on a tall phone and
   starts losing its last line on a short one — browser chrome can take
   150px off the visible height. Lift it, and on genuinely short frames
   stop demanding a full screen at all. */
@media (max-width:640px){
  .hero{padding-bottom:64px}
}
@media (max-width:640px) and (max-height:720px){
  .hero-zone{min-height:0}
  .hero{padding-top:230px;padding-bottom:44px}
  .hero .pillars{display:none}
}

/* ── press and focus ──────────────────────────────────────────────────
   Every piece of feedback on this site was a hover state, and a phone
   has no hover: a tap did nothing at all until the page changed. These
   are the press states, kept short and physical — the thing you touched
   gives a little, then lets go.

   Focus rings go on at the same time. Only .way had one, so keyboard
   users were navigating on whatever the browser happened to draw. */
@media (hover:none){
  .way:hover{padding-left:0}
  .way:hover::before{opacity:0}
  .way:hover::after{transform:scaleX(0)}
  .way:hover .arw{background:transparent;border-color:var(--line-strong);
    color:var(--steel);transform:none}
  .way:hover .n{color:var(--faint)}
  .way:hover .desc{color:var(--dim)}
  .opt:hover{border-color:var(--line);background:rgba(244,246,248,.85);transform:none}
}

.btn:active,.dock-cta:active{transform:scale(.975);background:var(--steel-hi)}
.btn-ghost:active,.fig-tab:active,.pick:active{transform:scale(.98);border-color:var(--steel)}
.opt:active{transform:scale(.985);border-color:var(--steel);background:rgba(107,139,164,.07)}
.way:active{background:rgba(107,139,164,.05)}
.way:active .arw{background:var(--steel);border-color:var(--steel);color:#FFFFFF}
.chips a:active,.sheet-links a:active,.ulink:active,.nav-links a:active{color:var(--steel)}
.hix:active{background:rgba(107,139,164,.06)}
.btn,.btn-ghost,.dock-cta,.opt,.pick,.fig-tab,.hix{
  transition:transform .12s ease,background .18s ease,border-color .18s ease,color .18s ease}
/* a press should feel instant even mid-transition */
.btn:active,.dock-cta:active,.opt:active,.btn-ghost:active,.pick:active,.fig-tab:active{
  transition-duration:.06s}

:focus-visible{outline:2px solid var(--steel);outline-offset:3px;border-radius:2px}
.btn:focus-visible,.dock-cta:focus-visible{outline-offset:4px}
.opt:focus-visible,.way:focus-visible{outline-offset:2px}

@media (prefers-reduced-motion:reduce){
  .btn:active,.btn-ghost:active,.dock-cta:active,.opt:active,.pick:active,.fig-tab:active{
    transform:none}
}

/* ── the foot of the page ─────────────────────────────────────────────
   From the FAQ down it was one flat near-black: the closing band sat at
   #0D1220 against #060A16, six points of luminance apart, and the footer
   had no background at all. The reference ends on a saturated gradient
   panel and a footer in its own colour; this palette has one accent, so
   it does the same job with tan at low strength and the blueprint grid
   the site already uses elsewhere.

   The closing band becomes a lit panel, the footer drops below paper so
   it reads as ground, and the sign-off stays the one bright strip. */
.closer{position:relative;overflow:hidden;
  /* was #12171F to #0E131A, which reads as black rather than as the brand.
     Same steel as the product band, a shade deeper, so the two dark ends of
     the site are the same colour. */
  background:
    radial-gradient(120% 100% at 88% 0%, rgba(134,180,230,.16), transparent 62%),
    linear-gradient(180deg, #2A4A6B 0%, #1B3450 100%);
  border-top:1px solid var(--line-strong)}
/* the same 24px grid as the blueprint, barely there */
.closer::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(rgba(107,139,164,.035) 1px,transparent 1px),
    linear-gradient(90deg,rgba(107,139,164,.035) 1px,transparent 1px);
  background-size:24px 24px;
  -webkit-mask-image:radial-gradient(120% 90% at 80% 0%, #000 20%, transparent 72%);
  mask-image:radial-gradient(120% 90% at 80% 0%, #000 20%, transparent 72%)}
.closer .sec{position:relative;z-index:1}
.closer .eyebrow{color:var(--steel)}

/* The contact page's title band. The field needs a full-width, positioned
   parent to sit in; `.subhero` itself is `.wrap`, which caps at 1160px. */
.subhero-zone{position:relative; overflow:hidden; background:var(--card)}
.subhero-zone .subhero{position:relative; z-index:1; border-bottom:0}
/* The wave behind the contact title. The canvas is drawn at half resolution
   and stretched, which is invisible on a soft gradient and a quarter of the
   fill cost. Held back so the dark heading over it stays the thing being read
   - it is ground, not subject. */
.wave-field{
  position:absolute; inset:0; z-index:0;
  width:100%; height:100%; display:block;
  pointer-events:none; opacity:.55;
}

/* ── bloom fields on the two call-to-action bands ──────────────────────
   Several soft radial blooms over the base, with a fine grain on top so the
   ramps do not band on a wide screen. The recipe is borrowed; the palette is
   not. The version it came from is built on a bright cyan that belongs to
   somebody else's brand, and this file already says every value on a dark band
   is one of ours.

   The grain is an inline feTurbulence - a few hundred bytes of SVG rather than
   a texture to fetch - and it is the one part of the original worth taking
   whole. */

/* The dark closer already had one bloom. Two more give it a light source with
   somewhere to fall off to, instead of a single wash across the corner. */
.closer{background:
  radial-gradient(78% 62% at 88% 4%, rgba(134,180,230,.20), transparent 60%),
  radial-gradient(56% 48% at 18% 92%, rgba(93,114,142,.26), transparent 62%),
  radial-gradient(40% 38% at 52% 30%, rgba(160,196,236,.10), transparent 64%),
  linear-gradient(180deg, #2A4A6B 0%, #1B3450 100%)}
/* Above the 24px grid but under the words. `overlay` keeps the grain out of
   the shadows, where it would read as noise rather than as texture. */
.closer::after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
  background-size:140px 140px;
  mix-blend-mode:overlay; opacity:.5}

/* The contact band stays light, and that is not a compromise: the enquiry form
   lives inside it, and a dark slab would mean restyling every field on the page
   to keep them legible. The bloom is the same idea at a tenth of the strength -
   enough that the band is not flat white behind the heading. */
.contact-band{background:
  radial-gradient(64% 54% at 88% 0%, rgba(107,139,164,.13), transparent 62%),
  radial-gradient(48% 44% at 6% 96%, rgba(93,114,142,.09), transparent 64%),
  var(--card)}
.contact-band{position:relative}
.contact-band > *{position:relative; z-index:1}
.contact-band::after{content:"";position:absolute;inset:0;z-index:0;
  pointer-events:none;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
  background-size:140px 140px;
  mix-blend-mode:multiply; opacity:.26}

/* Grain is decoration and it costs a repaint on every scroll frame for anyone
   who has asked for less of it. */
@media (prefers-reduced-motion:reduce){
  .closer::after,.contact-band::after{display:none}
}

/* ── the profile timeline ──────────────────────────────────────────────
   A rail down the left with entries hanging off it, each lighting as it
   arrives. The pattern came from a React component driven by a scroll-progress
   hook and an animation library; the effect is an IntersectionObserver and a
   transition, and this site has no build step to hang the other version on.

   The rail is drawn on the list rather than on each row, so it is one line
   that runs the height of the whole thing instead of a stack of segments that
   never quite meet. */
.tl-band{background:var(--paper); border-top:1px solid var(--line)}

.tl-soon{display:flex; flex-wrap:wrap; align-items:baseline; gap:12px;
  padding:14px 16px; margin-bottom:clamp(32px,4vw,52px);
  border:1px solid var(--line-strong); border-radius:2px; background:var(--card)}
.tl-soon-tag{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:10px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--steel)}
.tl-soon p{margin:0; font-size:13.5px; line-height:1.6; color:var(--dim);
  max-width:62ch}

.tl{position:relative; list-style:none; margin:0; padding:0 0 0 28px}
.tl::before{content:""; position:absolute; left:5px; top:8px; bottom:8px;
  width:1px; background:var(--line-strong)}
@media (min-width:820px){.tl{padding-left:0}}

.tl-item{position:relative; padding-bottom:clamp(36px,5vw,68px)}
.tl-item:last-child{padding-bottom:0}
@media (min-width:820px){
  .tl-item{display:grid; grid-template-columns:minmax(0,260px) minmax(0,1fr);
    gap:clamp(28px,4vw,64px)}
  /* The rail sits inside the left column on wide screens, so the dots line up
     with the headings rather than with the page edge. */
  .tl{padding-left:28px}
}

.tl-when{position:relative}
.tl-when h2{margin:0; font-size:clamp(19px,2.4vw,28px); font-weight:500;
  letter-spacing:-.02em; line-height:1.15; color:var(--faint);
  transition:color .5s ease}
.tl-item.on .tl-when h2{color:var(--ink)}

.tl-dot{position:absolute; left:-28px; top:.45em; width:11px; height:11px;
  border-radius:50%; background:var(--paper);
  border:1px solid var(--line-strong); box-sizing:border-box;
  transition:background-color .5s ease, border-color .5s ease}
.tl-item.on .tl-dot{background:var(--ink); border-color:var(--ink)}

/* Entries fade up as they arrive, and an entry that has arrived stays put: a
   rail that empties again on the way back up reads as a fault, not as
   progress. Anyone who has asked for less motion gets none of it. */
.tl-what{opacity:0; transform:translateY(8px);
  transition:opacity .55s ease, transform .55s ease}
.tl-item.on .tl-what{opacity:1; transform:none}
.tl-what p{margin:0 0 12px; font-size:15.5px; line-height:1.65; color:var(--dim);
  max-width:60ch}
.tl-what p:last-child{margin-bottom:0}
@media (max-width:819px){
  .tl-when{margin-bottom:12px}
}

/* Marked as unwritten in the markup and in the styling, so it cannot be
   mistaken for finished copy in a screenshot. */
.tl-todo{font-style:italic; color:var(--faint)}

@media (prefers-reduced-motion:reduce){
  .tl-what{opacity:1; transform:none; transition:none}
  .tl-when h2,.tl-dot{transition:none}
}

/* footer sits below paper so the page has a floor rather than fading out */
footer{background:#F4F6F8;border-top:1px solid var(--line)}
.foot{border-bottom:1px solid var(--line-soft)}

/* and the FAQ stops being the same slab as everything above it */
#faq{background:linear-gradient(180deg, transparent, rgba(107,139,164,.022))}

/* the plate is scaled a little on phones so the parallax has somewhere to
   travel without exposing an edge */
@media (max-width:820px){
  .hero-zone .plate img{will-change:transform}
}

/* the footer links were a single flex row with a 22px gap, so on a phone
   they broke mid-label — "CLIENT SIGN IN" over three lines — and the last
   one ran off the right edge */
@media (max-width:640px){
  .foot{flex-direction:column;align-items:flex-start;gap:22px;padding:28px 20px}
  .foot .links{flex-wrap:wrap;gap:14px 20px;width:100%}
  .foot .links a{white-space:nowrap}
  .foot .legal{line-height:1.6}
}

/* ── Tally: shader backdrop ───────────────────────────────────────────
   The product page opens on a moving field rather than a photograph —
   the atrium belongs to the consultancy, and Tally should not look like
   a fifth room of it. The canvas is decorative: if WebGL2 is missing, if
   the tab is hidden, or if less motion was asked for, the script never
   runs and this static ground is what shows. */
.shader-zone{position:relative;overflow:hidden;background:
  radial-gradient(120% 90% at 76% 10%, rgba(107,139,164,.09), transparent 64%),
  linear-gradient(180deg,#0C1118,#0A0E13)}
.shader-zone #shader{position:absolute;inset:0;width:100%;height:100%;
  display:block;opacity:0;transition:opacity 1.1s ease}
.shader-zone #shader.on{opacity:.85}
/* keeps the type off the brightest drifts without flattening the field */
.shader-zone::after{content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(255,255,255,0.8),rgba(255,255,255,0.41) 46%,transparent 78%),
    linear-gradient(0deg,rgba(255,255,255,0.63),transparent 40%)}
.shader-zone .subhero{position:relative;z-index:1;border-bottom:0}
.shader-zone{border-bottom:1px solid var(--line)}

/* The scrim above is shaped for the desktop layout, where type sits in the
   left half. On a phone the copy runs the full width, so the streaks were
   crossing the paragraph. Even coverage there instead. */
@media (max-width:820px){
  .shader-zone::after{background:
    linear-gradient(180deg,rgba(255,255,255,0.49) 0%,rgba(255,255,255,0.67) 40%,rgba(255,255,255,0.82) 100%)}
  .shader-zone #shader.on{opacity:.7}
}

/* ── Tally: what the client actually sees ─────────────────────────────
   The reference hero pairs a claim with a product panel underneath. The
   page already had a numbers card — a summary — but nothing that showed
   the interface. This is that panel, drawn in HTML rather than dropped
   in as a screenshot: no client named, no figure real, and it stays
   legible at any width instead of being a fixed-size image. */
.demo-zone{background:
  radial-gradient(110% 80% at 82% 0%, rgba(107,139,164,.07), transparent 60%),
  linear-gradient(180deg,#0C1118,#0A0E13);
  border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.demo-h{font-size:clamp(28px,4.2vw,46px);line-height:1.07;letter-spacing:-.03em;
  font-weight:500;margin:16px 0 0;max-width:19ch}
.demo-lede{font-size:16px;line-height:1.65;color:var(--dim);margin:16px 0 0;max-width:60ch}

.demo-frame{margin-top:clamp(28px,4vw,52px);border:1px solid var(--line-strong);
  border-radius:8px;overflow:hidden;background:var(--paper);
  box-shadow:0 30px 80px -40px rgba(0,0,0,.9)}
.demo-bar{display:flex;align-items:center;gap:12px;padding:11px 16px;
  background:var(--card);border-bottom:1px solid var(--line)}
.demo-dot{width:9px;height:9px;border-radius:50%;background:var(--steel);flex-shrink:0}
.demo-app{font-family:"Jost",system-ui,sans-serif;font-size:12px;font-weight:550;
  letter-spacing:.24em;margin-right:-.24em}
.demo-ws{font-size:12px;color:var(--faint)}
.demo-live{margin-left:auto;display:inline-flex;align-items:center;gap:7px;
  font-size:11.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--dim)}
.demo-live i{width:6px;height:6px;border-radius:50%;background:#5FA97F;
  box-shadow:0 0 0 3px rgba(95,169,127,.16)}

.demo-body{display:grid;grid-template-columns:176px 1fr}
.demo-side{display:grid;align-content:start;gap:2px;padding:16px 10px;
  border-right:1px solid var(--line);background:#0A0E13}
.demo-side span{display:flex;align-items:center;gap:8px;padding:9px 12px;border-radius:4px;
  font-size:13px;color:var(--faint)}
.demo-side span.on{background:rgba(107,139,164,.09);color:var(--ink)}
.demo-side b{margin-left:auto;font-size:11px;font-weight:600;color:#FFFFFF;
  background:var(--steel);border-radius:999px;padding:1px 7px}

.demo-main{padding:clamp(16px,2.2vw,26px)}
.demo-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;
  padding-bottom:18px;border-bottom:1px solid var(--line)}
.demo-k{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11.5px;
  letter-spacing:.1em;color:var(--faint)}
.demo-main h3{font-size:clamp(17px,2vw,22px);font-weight:500;letter-spacing:-.02em;
  color:var(--ink);margin:8px 0 0}
.demo-wait{flex-shrink:0;font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--steel);border:1px solid rgba(107,139,164,.42);border-radius:999px;padding:5px 11px}

.demo-steps{list-style:none;margin:0;padding:0}
.demo-steps li{display:grid;grid-template-columns:16px minmax(96px,auto) 1fr auto;
  gap:14px;align-items:baseline;padding:14px 0;border-bottom:1px solid var(--line-soft)}
.demo-steps b{font-size:14px;font-weight:600;color:var(--ink)}
.demo-steps em{font-style:normal;font-size:13.5px;color:var(--dim)}
.demo-steps .t{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;color:var(--faint)}
.demo-steps .s{width:9px;height:9px;border-radius:50%;align-self:center;
  border:1px solid var(--line-strong)}
.demo-steps .done .s{background:var(--steel-dim);border-color:var(--steel-dim)}
.demo-steps .now .s{background:var(--steel);border-color:var(--steel);
  box-shadow:0 0 0 4px rgba(107,139,164,.16)}
.demo-steps .now b{color:var(--steel)}
.demo-steps .next{opacity:.5}

.demo-act{display:flex;align-items:center;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-top:18px}
.demo-sig{font-size:13px;color:var(--faint)}
.demo-btns{display:flex;gap:10px}
.demo-btns i{font-style:normal;font-size:12.5px;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;padding:9px 16px;border-radius:3px}
.demo-btns .ghost{border:1px solid var(--line-strong);color:var(--dim)}
.demo-btns .go{background:var(--steel);color:#FFFFFF}
.demo-note{font-size:12px;color:var(--faint);margin-top:14px;letter-spacing:.04em}

/* The real product: the demo video and the screenshot gallery. Frames match
   the site's ledger language — 1px line, 2px radius, no shadows. */
.demo-video{display:block;width:100%;margin-top:clamp(28px,4vw,52px);
  border:1px solid var(--line-strong);border-radius:2px;background:#fff}
/* The demo has no audio, so the captions carry the whole commentary and are on
   by default. The browser default is a heavy black box in a face that is not
   ours; this keeps the panel dark enough to stay legible over a light UI
   recording without turning into a slab across the picture. */
.demo-video::cue{
  background:rgba(11,15,25,.82); color:#fff;
  font-family:"Switzer","Neue Montreal",system-ui,sans-serif;
  font-size:.85em; line-height:1.45;
}
.tshots{display:grid;grid-template-columns:1fr;gap:clamp(64px,8vw,110px);
  margin-top:clamp(40px,5.5vw,72px)}
.tshot{margin:0}
.tshot img{display:block;width:100%;height:auto;border:1px solid var(--line-strong);border-radius:2px;background:#fff}
/* The explanatory column was cut on 2026-08-23: the screenshot under each of
   these already shows what the sentence described. This was a two-column grid
   with the title in a 360px cell, which left the heading squashed against a
   half-empty row once the sentence went. One column now, so the title runs to
   its natural width. */
.tshot figcaption{margin:0 0 22px}
.tshot figcaption b{display:block;font-size:clamp(21px,2.3vw,30px);font-weight:600;
  color:var(--ink);letter-spacing:-.02em;line-height:1.15;text-wrap:balance}

/* Video chapter strip */
.vchapters{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.vchapters button{height:34px;padding:0 16px;background:transparent;border:1px solid var(--line-strong);
  border-radius:2px;font-size:12px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink);cursor:pointer;transition:border-color .15s ease,color .15s ease}
.vchapters button:hover{border-color:var(--steel);color:var(--steel)}
.vhint{display:none}
@media (max-width:760px){
  .vchapters{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}
  .vchapters button{flex:0 0 auto}
  .vhint{display:block}
}
/* The screenshots were wrapped in a link to the raw jpg, which opened a
   1600px image over the page. Unwrapped 2026-09-01; picture still needs
   display:block or it sits inline and leaves a descender gap under it. */
.tshot picture{display:block}

/* Security section */
.trust{border-top:1px solid var(--line)}
.trust-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4vw,48px) clamp(32px,5vw,64px);
  margin-top:clamp(32px,4.5vw,56px)}
.trust-item b{display:block;font-size:16.5px;font-weight:600;color:var(--ink);letter-spacing:-.01em}
.trust-item p{margin:8px 0 0;font-size:14.5px;line-height:1.7;color:var(--dim)}
.trust-item a{border-bottom:1px solid var(--line-strong)}
.trust-tech{margin-top:8px !important;font-size:12px !important;line-height:1.6 !important;
  color:var(--faint) !important;font-family:"IBM Plex Mono",ui-monospace,monospace;letter-spacing:.01em}
.trust-close{margin-top:clamp(36px,5vw,56px);max-width:760px;font-size:14.5px;line-height:1.7;color:var(--dim)}
.trust-close p{margin:0 0 14px}
.trust-close p:last-child{margin-bottom:0}
.trust-close b{color:var(--ink);font-weight:600}
@media (max-width:760px){.trust-grid{grid-template-columns:1fr}}

@media (max-width:820px){
  .demo-body{grid-template-columns:1fr}
  .demo-side{display:flex;gap:6px;overflow-x:auto;border-right:0;
    border-bottom:1px solid var(--line);padding:10px;scrollbar-width:none}
  .demo-side::-webkit-scrollbar{display:none}
  .demo-side span{white-space:nowrap;font-size:12.5px}
  .demo-steps li{grid-template-columns:14px 1fr auto;gap:6px 12px}
  .demo-steps em{grid-column:2/-1}
  .demo-head{flex-direction:column;gap:12px}
  .demo-act{flex-direction:column;align-items:flex-start}
  .demo-btns{width:100%}
  .demo-btns i{flex:1;text-align:center;padding:13px 16px}
}

/* The scrim over this canvas was tuned for the streak shader that used to be
   here: thin bright lines that needed holding down. A mesh gradient is soft
   and low-contrast to begin with, and the same scrim flattened it to nothing
   — the field was running the whole time and could not be seen. Much lighter,
   and only where the type actually sits. */
.shader-zone::after{background:
  linear-gradient(90deg,rgba(255,255,255,0.63),rgba(255,255,255,0.23) 44%,transparent 72%)}
.shader-zone #shader.on{opacity:1}
@media (max-width:820px){
  .shader-zone::after{background:
    linear-gradient(180deg,rgba(255,255,255,0.26) 0%,rgba(255,255,255,0.41) 44%,rgba(255,255,255,0.64) 100%)}
  .shader-zone #shader.on{opacity:1}
}

/* the home hero is the atrium photograph again — the shader field it briefly
   carried belongs to Tally, where a generated surface suits a software page.
   Reversing this is one element: swap the <picture> for <canvas id="shader">
   and load assets/shader-hero.js. */

/* ── the hero pattern ────────────────────────────────────────────────
   The artwork itself, tiled — not a reconstruction. Building it from one mark
   on a grid lost both the interlock and the three flat fills, which is why it
   read as scattered triangles instead of a woven surface.

   The strip is 311px, the measured horizontal period of the artwork, repeated
   across; its seam error is 5.5 of 255. It does not repeat vertically, so the
   height is scaled to the hero rather than tiled.

   The opacities are measured. The pattern's lightest fill is #9A9AA8 and white
   type over it at full strength gives 2.78 against a 4.5 floor; .70 is where it
   clears. The resting field is .30 (12.2) and the area under the pointer
   reaches .62 (5.9), so the headline holds even with the bright area sitting
   directly behind it. */

/* ── the hero field ──────────────────────────────────────────────────
   A canvas drawn from polygons, not a picture. Every earlier version tiled a
   strip of the artwork and cut each mark out as a sprite, and every problem
   came from that: bounding boxes that read as squares, edges stripped of
   their anti-aliasing, blur wherever the tile was scaled, seams. None of it
   exists once the marks are geometry. assets/hero-field.js owns the motion.

   touch-action and the absent pointer come from Dixon's spec: a dragging
   finger should still scroll, and the field is decoration, so it takes no
   pointer of its own — the hero listens instead. */
#ddx-pattern,.ddx-field{
  position:absolute; inset:0; z-index:0;
  width:100%; height:100%; display:block;
  touch-action:none; pointer-events:none;
}
/* The closing band had the hero's tessellation for a few hours on 2026-08-26.
   Dixon asked for it, saw it and asked for it gone. It is the bloom's band: the
   question is the whole of it, and a pattern behind a question competes with
   reading it. The hero keeps the field; this one does not. */

/* ── the works-with river, back at the hero's foot ────────────────────
   Removed during the rebuilds; Dixon asked for it back. The strip is the
   systems the work runs inside — never clients — and the old JS that sized
   the animation is replaced by one CSS rule: the track holds two copies of
   the list and slides half its own width, which loops seamlessly at any
   content length. */
/* The strip used to run the full width of the page along the bottom edge,
   which made it a second object with its own axis. Dixon: move it into the
   middle with the words, and cut it — it is too long. So it is now a block
   on the same centre line as the copy, about the width of the headline, and
   the rail is short enough that the tools read as a group rather than as a
   ticker crossing the screen. */
.hero-zone .works{
  position:relative; z-index:2;
  margin:0 auto; width:min(760px, 92vw);
  /* nothing in the strip is a link, and on phones it rides over the section
     cards — it must never eat their taps */
  pointer-events:none;
  background:none;
  border-top:0;
}
.hero-zone .works-in{
  max-width:none;
  padding:14px clamp(22px,4.4vw,88px) 16px;
  display:flex; align-items:center; gap:18px;
}
/* The strip sits outside the clearing — it is below the copy the zone is
   measured from — so its label is on full-strength field. At --faint it
   measured 4.14 there, at --dim 2.22 once the clearing was pulled in to the
   ink. Ink and a heavier weight, which is 5.2 against the darkest pixel
   under it. */
.hero-zone .works .cap{color:var(--ink); font-weight:600}
/* Three rows, on Dixon's note to make it read like the tool grid on the
   Tally sign-in page. Grouped rather than shuffled: what the day is talked
   in, what money moves through, what goods and customers move through.
   The middle row runs the other way, which stops the three reading as one
   thick band sliding sideways.

   Every row carries all 28, each starting a third of the way further into
   the list. Splitting them into three short lists put the same nine tools
   across the rail two and a half times, and the repeat was the first thing
   you saw.

   Looping: three copies per row, and the keyframe travels exactly one copy
   (100/3%), so the snap-back lands on an identical frame. Seamless up to
   two copy-widths — about 2900px — where the old -50% translate only worked
   if one copy happened to be wider than the rail. */
.hero-zone .works-in{display:block; padding:10px 0 18px; max-width:none}
.hero-zone .works .cap{display:block; margin-bottom:10px; text-align:center}
.hero-zone .river{flex:none; width:100%;
  -webkit-mask-image:linear-gradient(90deg,transparent,black 12%,black 88%,transparent);
  mask-image:linear-gradient(90deg,transparent,black 12%,black 88%,transparent)}
.hero-zone .river-track{justify-content:flex-start}
.hero-zone .river + .river{margin-top:7px}
.river-track{animation:river 96s linear infinite}
.river .river-track.rev{animation:river-rev 118s linear infinite}
.hero-zone .river.r3 .river-track{animation-duration:108s}
@keyframes river{from{transform:none} to{transform:translateX(-33.3333%)}}
@keyframes river-rev{from{transform:translateX(-33.3333%)} to{transform:none}}
@media (max-width:700px){
  /* two rows on a phone, not three — the strip sits under the copy and a
     third row starts pushing the promise off the first screen */
  .hero-zone .river.r3{display:none}
  /* The headline is the page on a phone: one screen, one statement. It was
     running at the same clamp as the desktop minimum, which on a 390pt
     screen is a caption. */
  .hero-zone h1.setwide{font-size:clamp(46px,13.4vw,62px); line-height:1.0}
  .hero-zone .hero .sub{font-size:16.5px; margin-top:22px; max-width:34ch}
  .hero-zone .hero .pillars{margin-top:30px}
}


/* ── the hero, centred ────────────────────────────────────────────────
   Dixon's reference: the promise sits big in the middle of the page and
   says what the client gets. Two things follow from centring it.

   The veil was a left-hand column wash, which is the wrong shape once the
   words are in the middle — so the copy carries its own plate instead: a
   soft white pad behind the block, wide enough to clear the longest line.
   That is his instruction directly, "if the word is being blocked by the
   background add an extra background under the word", and it is also the
   only way to keep the field visible at the edges while the type stays at
   full contrast.

   One action. The hero had two buttons and a row of section links competing
   with each other; a page with five equal calls to action gets none of them
   pressed. Everything but the discovery chat moves below the fold. */
/* The facet column is hidden in this layout, but the grid still reserved a
   250-300px track for it — so "centred" was centred in column one, a long way
   left of the page. One column, and the plate centres on the viewport. */
.hero-zone .hero{max-width:none}

/* Side by side, on Dixon's call. Stacked, the promise and the wall of tools
   were two things you read one after the other and the page had to be tall
   enough for both; beside each other they are one statement — what we do,
   and where we do it — and the eye takes them in together.
   Below 1001px they go back to stacked and centred: at that width a second
   column is a 150px gutter, which is worse than either. */

.hero-zone .hero-say{
  /* an explicit width, not auto margins: auto margins on a grid item switch
     the box to fit-content, which was holding the headline to 539px */
  width:min(1160px, 94vw);
  max-width:100%;
  justify-self:center;
  margin:0 auto;
  text-align:center;
  position:relative;
  /* The clearing is measured off this box, so its padding is how far the
     field is pushed away from the type. Held wide, a bigger headline just
     cleared more of the artwork; held tight, the pattern comes back up to
     the letterforms and the pieces near them still have room to lift. */
  padding:clamp(4px,0.6vw,8px) clamp(6px,0.8vw,12px);
}
/* the plate: not a hard box, a pool of page colour that fades out before it
   reaches an edge, so it never reads as a card sitting on the pattern */
/* No plate. A white panel over the field always read as a second object
   sitting on the artwork; the clearing behind the copy is cut into the
   pattern itself in hero-field.js, shape by shape, so its edge is made of
   the artwork's own triangle edges. One piece, not two. */
.hero-zone h1.setwide{
  margin-inline:auto;
  /* Bigger, on his call. The reference sets its promise at the scale of a
     headline in print, not a page title — it is the first and often only
     thing read, so it takes the room. */
  font-size:clamp(44px,9.6vw,132px);
  letter-spacing:-.016em;
  line-height:1.0;
}
.hero-zone .hero .sub{margin:30px auto 0; max-width:52ch; text-align:center; font-size:18px}
.hero-zone .hero-ctas{justify-content:center; margin-top:34px}
.hero-zone .hero-note{text-align:center}
/* the pillar row and its short rule are left-anchored everywhere else */
.hero-zone .hero .pillars{justify-content:center; margin-top:44px}
.hero-zone .hero .pillars::before{left:50%; transform:translateX(-50%)}
/* the second action and the section index belong below the fold */
.hero-zone .hero-ctas .ulink{display:none}
.hero-zone .hero-facets{display:none}
@media (max-width:900px){
  .hero-zone .hero-say{padding:26px 20px}
}


/* ── the header, Kyan's shape ─────────────────────────────────────────
   Dixon: copy this, just keep contact. Logo left, the menu on the centre
   line, one button right. The four inline links and the sign-in button are
   gone from the bar entirely — a header with six things in it asks the
   visitor to choose before they have read anything. */
.nav-in{display:grid; grid-template-columns:1fr auto 1fr; align-items:center}
.nav-links{justify-self:end; display:flex; align-items:center; gap:14px}
.nav-burger{display:inline-flex !important; justify-self:center;
  width:56px; height:56px; border:0; background:none; color:var(--ink)}
.nav-burger:hover{border-color:transparent}
/* a chevron, not a hamburger: the menu drops down, so the control points
   the way the panel travels */
.nav-burger svg{width:34px; height:34px; color:inherit}
.nav-burger:hover{color:var(--steel)}
.nav-cta{height:52px; padding:0 32px; border-radius:999px;
  font-size:13px; letter-spacing:.14em}
/* second door in the bar: the calculator, outlined so Talk to us stays the
   one filled button. Hidden on phones, where the menu carries it. */
.nav-ghost{display:inline-flex; align-items:center; height:52px; padding:0 26px; border-radius:999px;
  border:1px solid var(--line-strong); color:var(--ink) !important; font-size:13px; letter-spacing:.14em; white-space:nowrap}
.nav-ghost:hover{border-color:var(--ink); color:var(--ink) !important}
@media (max-width:640px){
  .nav-burger{width:48px; height:48px}
  .nav-burger svg{width:28px; height:28px}
  .nav-cta{height:46px; padding:0 22px; font-size:12px}
  .nav-ghost{display:none}
}

/* the menu itself: the list from the reference, without its numbers */
.sheet-links{margin-top:clamp(20px,5vh,56px)}
.sheet-links a{display:flex; align-items:baseline; gap:clamp(16px,3vw,42px);
  padding:clamp(8px,1.4vh,16px) 0; font-size:clamp(34px,7vw,72px);
  font-weight:400; letter-spacing:-.03em; line-height:1.06; color:var(--ink)}
.sheet-links a:hover .st{color:var(--steel)}

/* ── who we are / what we do, the reference's section head ────────────
   A quiet label out in the left margin, the statement at reading size in
   the column beside it. It is the same object twice, so it is one rule. */
.kyn{border-top:1px solid var(--line); background:var(--paper)}
.kyn-in{display:grid; grid-template-columns:minmax(0,1fr); gap:18px}
@media (min-width:900px){
  .kyn-in{grid-template-columns:200px minmax(0,1fr); gap:clamp(30px,5vw,90px)}
}
.kyn-label{font-size:11.5px; font-weight:500; letter-spacing:.16em;
  text-transform:uppercase; color:var(--faint); margin:6px 0 0}
/* The measure moves from ch to px: a ch in Switzer is about 0.31em, so 20ch
   was a narrow column that got narrower as the type grew. Size is set once,
   for the whole site, further down. */
.kyn-say{color:var(--ink); margin:0; max-width:min(900px,100%)}
.ways-head .kyn-say{max-width:min(1040px,100%)}
/* ── the method, under "who we are" ───────────────────────────────────
   A numbered list, because the number is the argument: automation is the
   fifth thing, and a reader who takes nothing else from this section
   should take the position of that line. The numerals are the mono face
   the site already uses for indices and figure numbers, so the list reads
   as a procedure rather than as five more paragraphs.

   The rule sits between the steps, not around the block: a bordered card
   would make it a feature box, and this is the section's argument, not an
   aside to it. */
.approach-lede{font-size:18px; line-height:1.6; color:var(--dim);
  margin:clamp(16px,1.8vw,22px) 0 0}

.method{list-style:none; counter-reset:step; margin:clamp(28px,3.4vw,44px) 0 0;
  padding:0; max-width:56ch; border-top:1px solid var(--line)}
.method li{counter-increment:step; display:grid; grid-template-columns:2.6em 1fr;
  gap:4px clamp(12px,1.4vw,20px); padding:clamp(14px,1.6vw,20px) 0;
  border-bottom:1px solid var(--line)}
.method li::before{content:counter(step,decimal-leading-zero); grid-row:1/3;
  font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px; font-weight:500;
  letter-spacing:.08em; color:var(--steel-dim); padding-top:.42em}
.method b{font-size:clamp(17px,1.5vw,20px); font-weight:600; letter-spacing:-.012em; color:var(--ink)}
.method span{font-size:15.5px; line-height:1.55; color:var(--dim); max-width:44ch}
/* the last step is the one being argued for, so it gets the one accent */
.method li:last-child b{color:var(--steel-dim)}
@media (max-width:640px){
  .method li{grid-template-columns:2.2em 1fr}
  .method span{font-size:15px}
}

/* ── the method's travelling light ────────────────────────────────────
   The spine the old five-step layout had, adapted for the numbered list:
   a track to the left of the numbers, filled to wherever the reader has
   got to, each number lighting as the line reaches it. The fill height
   arrives through --spine-h from the scroll handler — an ol may contain
   only li, so the fill is a pseudo-element rather than a span. */
.method{position:relative; padding-left:clamp(20px,2.4vw,30px)}
.method::before{content:""; position:absolute; left:0; top:14px; bottom:14px;
  width:1px; background:var(--line)}
.method::after{content:""; position:absolute; left:0; top:14px; width:1px;
  height:var(--spine-h,0px); max-height:calc(100% - 28px);
  background:linear-gradient(180deg,rgba(107,139,164,.35),var(--steel));
  box-shadow:0 0 10px rgba(107,139,164,.45)}
.method li::before{transition:color .35s ease}
.method li.lit::before{color:var(--steel)}
@media (prefers-reduced-motion:reduce){
  /* the handler never runs, so the track would sit at zero forever —
     give those visitors the finished page instead of a stub */
  .method{padding-left:0}
  .method::before,.method::after{display:none}
}

/* ── the stance, said on its own ground ───────────────────────────────
   Five white sections in a row is a wall, however good the words. The one
   sentence the firm leads with gets the dark plate the closing band uses,
   so the middle of the page has an anchor and the refusal reads as a
   position rather than another paragraph. Palette matches .closer — the
   page has one dark, not two. */
.stance{position:relative; overflow:hidden; border-top:1px solid var(--line);
  background:linear-gradient(158deg,#122334 0%,#0C1826 46%,#0B0F14 100%)}
.stance::before{content:""; position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(560px 320px at 82% 12%,rgba(107,139,164,.14),transparent 68%)}
.stance .sec{position:relative}
.stance .kyn-label{color:#8FAECB}
.stance .kyn-say{color:#F2F6FA}
.stance .stance-p{color:#B7C4D4}
.stance .stance-p.pt{color:#8FAECB}

/* ── the tally band's picture ─────────────────────────────────────────
   The band names a product you look at, so it shows the thing: the
   tally.html walk-through frame at column width. .mini drops the frame's
   sidebar — at 540px the rail is noise, and the run is the argument. */
/* The screenshot is the argument on this band, and the layout kept shrinking
   it. Sitting as the second column of `.tally-duo` it got what was left after
   a 360px text column and the gap — around 680px of an 1160px container, for
   a dashboard whose whole point is detail.
   It is now a sibling of the copy rather than a column beside it: the text
   reads at its natural measure and the picture takes the full container
   underneath, which is roughly half as wide again. */
.tally-shot{display:block; margin-top:clamp(28px,4vw,56px)}
.tally-still{display:block; width:100%; height:auto;
  border:1px solid var(--line-strong); border-radius:2px; background:#fff}
/* A dashboard is detail at a distance. A little shadow lifts it off the band
   so it reads as a screen rather than a printed panel. */
@media (min-width:760px){
  .tally-shot .tally-still{box-shadow:0 18px 44px -28px rgba(11,15,25,.42)}
}
.demo-frame.mini .demo-side{display:none}
.demo-frame.mini .demo-body{grid-template-columns:1fr}

.kyn-body > .pill{margin-top:clamp(24px,3vw,38px)}
.pill{display:inline-flex; align-items:center; min-height:48px; padding:0 26px;
  border:1px solid var(--line-strong); border-radius:999px;
  font-size:11px; font-weight:500; letter-spacing:.14em; text-transform:uppercase;
  color:var(--ink) !important; transition:border-color .2s, background-color .2s}
.pill:hover{border-color:var(--steel); background:var(--steel-wash)}
/* the same faces as the about page. They scale with the column rather than
   sitting at a fixed size: at 180px against a 44ch paragraph the two founders
   read as thumbnails attached to text, and this section is the only place on
   the home page where a visitor finds out that DDX is two identifiable people
   rather than a brand. */
.kyn-cols .face{display:block; width:100%; height:auto; max-width:clamp(240px,30vw,440px);
  aspect-ratio:1; object-fit:cover; border-radius:50%;
  background:var(--card); margin:0 0 16px}

/* ── what we do, as three stages ──────────────────────────────────────
   The seven cards this replaces were a menu; three stages are a sequence,
   and the numbers say so. Each carries a second paragraph set quieter than
   the first: the commitment rather than the description — what we will not
   do, what we will not replace, what you will not be handed. */
/* an answer that runs to three paragraphs needs the spacing, and its last
   line is the point rather than more detail — same quieter treatment the
   stages give their closing commitment */
/* ── the stance, first thing after the hero ──────────────────────────
   Left on paper rather than tinted: the Tally band further down is already
   tinted, and two tinted bands on one page stop being emphasis and start
   being stripes. The closing line takes the quieter colour the stages use
   for their commitments, so the three places on this page that promise the
   same thing look like they were written by the same company. */
.stance-p{font-size:18px; line-height:1.62; color:var(--dim); max-width:58ch;
  margin:clamp(16px,1.8vw,22px) 0 0}
.stance-p.pt{color:var(--faint)}

/* ── Tally, as a band between the work and the questions ─────────────
   Tinted, so it reads as a different kind of statement from the stages
   above it: those are what we do, this is what you get to hold. */
.tally-band{background:var(--card); border-top:1px solid var(--line)}
.tally-lede{font-size:18px; line-height:1.62; color:var(--dim); max-width:54ch;
  margin:clamp(18px,2vw,24px) 0 0}
.tally-acts{margin-top:clamp(24px,3vw,34px)}

.faq-grid p + p{margin-top:12px}
.faq-grid .pt{color:var(--faint)}

.stage{padding:clamp(22px,2.6vw,32px) 0; border-top:1px solid var(--line)}
.stage .n{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px;
  font-weight:500; letter-spacing:.1em; color:var(--steel-dim)}
.stage h3{font-size:clamp(24px,2.8vw,36px); font-weight:500; letter-spacing:-.022em;
  color:var(--ink); margin:8px 0 14px}
.stage p{font-size:17px; line-height:1.6; color:var(--dim); margin:0; max-width:62ch}
.stage .pt{margin-top:12px; color:var(--faint)}
.stage p + p{margin-top:12px}

/* ── what you can actually engage us for ──────────────────────────────
   Deliberately a list and not cards. The seven cards this section used to
   carry were a menu, and a menu asks a buyer to choose before he knows
   what he needs; the three stages above answer how the work runs, and this
   answers what arrives. Two columns on a wide screen so it reads as a
   short, finite set rather than a scroll. */
.deliver{margin-top:clamp(34px,4vw,56px); padding-top:clamp(24px,3vw,34px);
  border-top:1px solid var(--line)}
.deliver-lab{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:11px;
  font-weight:500; letter-spacing:.14em; text-transform:uppercase;
  color:var(--steel-dim); margin:0 0 clamp(16px,2vw,24px)}
.deliver ul{list-style:none; margin:0; padding:0;
  display:grid; gap:clamp(18px,2.2vw,28px) clamp(28px,4vw,64px)}
@media (min-width:820px){.deliver ul{grid-template-columns:1fr 1fr}}
.deliver li{display:flex; flex-direction:column; gap:4px}
.deliver b{font-size:17px; font-weight:600; letter-spacing:-.01em; color:var(--ink)}
.deliver span{font-size:15.5px; line-height:1.55; color:var(--faint); max-width:46ch}
@media (min-width:900px){
  /* Number in the margin, so the sequence is readable down the left edge.
     grid-row:1/-1 and an explicit column for everything else, not a fixed
     row count — the first version assumed three children and pitched the
     fourth paragraph into the number column as a one-word-wide strip the
     moment a stage grew. */
  .stage{display:grid; grid-template-columns:5rem 1fr; gap:0 clamp(20px,2.4vw,36px)}
  .stage .n{grid-row:1/-1; padding-top:.7em}
  .stage > *:not(.n){grid-column:2}
  .stage h3{margin-top:0}
}
.kyn-cols{display:grid; gap:clamp(26px,4vw,56px); margin-top:clamp(48px,7vw,96px)}
@media (min-width:760px){.kyn-cols{grid-template-columns:1fr 1fr}}
.kyn-cols h3{font-size:clamp(20px,1.9vw,26px); font-weight:500; letter-spacing:-.012em;
  color:var(--ink); margin:0 0 12px}
.kyn-cols p{font-size:17px; line-height:1.6; color:var(--dim); margin:0 0 18px; max-width:42ch}
.ways-head{display:grid}
.ways-head .ways-lede{margin-top:20px; max-width:56ch}


/* ── the hero, centred, one line of tools ─────────────────────────────
   Side by side was tried and pulled: Dixon wants the promise in the middle
   of the page with nothing beside it, and the tools as a single line under
   it. The pillar row goes with it — the hero says one thing now. */
@media (min-width:1001px){
  .hero-zone .hero{grid-template-columns:minmax(0,1fr); gap:0}
}
.hero-zone .hero-say{justify-self:center}
.hero-zone .hero .pillars{display:none}
/* tight under the button, not floating at the bottom of the screen */
.hero-zone .works{margin:clamp(18px,2.2vw,28px) auto 0; width:min(1000px,94vw)}
.hero-zone .works-in{padding:0}
.hero-zone .works .cap{text-align:center; margin-bottom:14px}
/* one row, not three */
.hero-zone .river.r2, .hero-zone .river.r3{display:none}
.hero-zone .river img{width:66px;height:66px;padding:10px;border-radius:10px}
.hero-zone .river-track{gap:14px}
@media (max-width:700px){
  .hero-zone .river img{width:52px;height:52px;padding:10px}
  .hero-zone .river-track{gap:10px}
}

/* ── the home hero, set left and named ────────────────────────────────
   The centred column was built for a three-word headline. This one is a
   sentence, and a long sentence centred over a 1160px measure makes the
   eye hunt for the start of every line — the one thing a first screen
   cannot afford. Everything in the hero anchors left instead; the column
   itself stays centred on the page, so the composition does not move.

   The .setwide rules are untouched: Tally, About and Contact still use
   the Jost caps treatment, and this class only replaces it on the home
   page, where the headline had to carry a subject and an object. */
.hero-zone .hero-say{text-align:left}
/* No font-size here. `.hero h1 {font-size: clamp(38px, 7.6vw, 76px)}` is two
   selectors to this one's one and governs the desktop ramp whatever this
   rule says — a size written here would be dead code that reads as if it
   were in charge. The phone ramp is set in the phone-type block, where it
   can out-specify the 33px rule that lives there. */
/* The hero carries three explained beats under the headline now, so the
   headline cannot also take 76px over five lines — that pushed the logo
   strip off the first screen entirely. `.hero h1` is two selectors and wins
   over a bare class, so the size is set at that weight here. */
/* Both properties belong on this selector, not on the bare class: line 213
   sets `.hero h1{max-width:14ch}` with no media query, and two selectors beat
   one. That 14ch measured 468px and held the headline at five lines, which
   put the logo strip past the fold. Set in px so the measure does not depend
   on the width of one glyph in one typeface. */
.hero h1.hero-claim{font-size:clamp(34px,4.3vw,58px); max-width:min(720px,100%)}
.hero-claim{
  line-height:1.06; letter-spacing:-.032em; font-weight:500;
  margin-top:22px}
/* the trade the reader is looking for, given the one accent on the screen */
.hero-claim em{font-style:normal; color:var(--steel-dim)}
/* ── Find it. Build it. Run it. ───────────────────────────────────────
   The three beats the site opened with originally, brought back with the
   thing they were missing: an object each. On their own they were three
   pronouns and a reader had to guess what "it" was; under a headline that
   supplies the noun, and with a sentence attached to each, the rhythm does
   what it was always meant to do.

   The line itself is set small and tracked, not large — it is a label for
   the three that follow, and a second big statement under the headline
   would fight it. */
.triad-say{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px;
  font-weight:500; letter-spacing:.14em; text-transform:uppercase;
  color:var(--steel-dim); margin:clamp(20px,2.4vw,30px) 0 0}
.triad{list-style:none; margin:clamp(14px,1.6vw,20px) 0 0; padding:0;
  display:grid; gap:clamp(10px,1.2vw,16px); max-width:52ch}
.triad li{display:grid; grid-template-columns:auto 1fr;
  gap:4px clamp(10px,1.2vw,16px); align-items:baseline}
.triad b{font-size:17px; font-weight:600; letter-spacing:-.01em; color:var(--ink);
  white-space:nowrap}
.triad b::after{content:""; display:inline-block; width:1px; height:.85em;
  margin:0 0 -.08em clamp(10px,1.2vw,16px); background:var(--line-strong)}
.triad span{font-size:17px; line-height:1.45; color:var(--faint)}
@media (max-width:640px){
  /* stacked on a phone: the rule between label and line has nowhere to go
     in a 350px column, and two lines read faster than a wrapped one */
  .triad li{grid-template-columns:1fr; gap:1px}
  .triad b::after{display:none}
  .triad b, .triad span{font-size:16px}
}
.hero-zone .hero .sub{margin:24px 0 0; max-width:46ch; text-align:left; font-size:18px}
.hero-zone .hero-ctas{justify-content:flex-start; align-items:center;
  flex-wrap:wrap; gap:clamp(16px,2vw,28px); margin-top:34px}
.hero-zone .hero-ctas .hero-note{margin-top:0; text-align:left}

/* ── the systems, said rather than shown ──────────────────────────────
   The tiles are gone and the plate with them: a white rounded box with a
   border, on a white page, twenty-eight times, was the part that read as
   clutter — not the logos inside it. The marks sit straight on the page in
   their own colours and wrap as one block, so the row reads as a list of
   names rather than as a strip of small buttons. Nothing animates: this is
   the last thing on the first screen, and motion here competes with the
   claim it is supposed to support. */
/* The strip sits close under the button, not floated halfway down the empty
   half of the screen. Seventy pixels of margin plus twenty-four of padding
   put a hundred-pixel band of bare pattern between the call to action and
   the rule, which read as two separate things rather than as the bottom of
   one hero. */
/* No rule above the strip. `--line` is #E6E6E6, which is a separator on a
   white page and a white streak across the hero's pattern - and the pattern is
   the thing the eye is meant to follow down to the marks. The gap does the
   separating; a line was only ever doing it twice. */
.systems{width:min(1160px,94vw); margin:clamp(26px,2.8vw,42px) auto 0;
  padding-top:0}
.systems-cap{font-size:10.5px; font-weight:500; letter-spacing:.16em;
  text-transform:uppercase; color:var(--faint); margin:0}
/* One row that keeps moving, so the set stays one line at every width
   instead of breaking into an orphan row somewhere between 900 and 1160.
   The mask at both ends is what says the list continues past the frame. */
/* the left fade is short and the right one long: marks arrive from the
   right, where a slow fade reads as "the list continues", and leave at the
   left, where the rail's edge is the same line the caption and the headline
   are set to — a hard cut there would look like a broken image, and a long
   fade would blur an alignment the whole hero is built on */
.systems-rail{overflow:hidden; margin-top:20px;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 3%,#000 82%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 3%,#000 82%,transparent)}
/* The duration is a lap time, not a speed, so it has to be recomputed every
   time the set grows or the roll silently speeds up. Forty-eight marks at
   82px each make a 7872px track; half of that at the 19px/s this started
   at is 204 seconds. */
.systems-track{display:flex; align-items:center; width:max-content;
  animation:systems-roll 204s linear infinite}
/* margin, not flex gap: the track has to be exactly two identical copies
   for a -50% slide to be seamless, and a flex gap gives 55 gaps across 56
   marks — half that width is not one whole copy, so the loop jumps */
.systems-track img{width:52px; height:52px; object-fit:contain; flex-shrink:0;
  margin-right:clamp(18px,2.2vw,30px)}
@keyframes systems-roll{from{transform:none} to{transform:translateX(-50%)}}
.systems-rail:hover .systems-track{animation-play-state:paused}
@media (prefers-reduced-motion:reduce){
  .systems-track{animation:none}
}
@media (max-width:640px){
  .systems{margin-top:30px; padding-top:0}
  .systems-rail{margin-top:16px}
  .systems-track img{width:40px; height:40px; margin-right:18px}
}

/* ── opening the menu ─────────────────────────────────────────────────
   It used to fade 6px in 220ms, which on a full-screen panel reads as a
   flicker. The reference drops the whole sheet from the top on a long
   ease and lets the list arrive after it, one line at a time — so the
   panel is the movement and the items are the detail. */
.sheet{
  padding:0 clamp(22px,4.4vw,88px) 44px;
  opacity:1;
  transform:translateY(-101%);
  transition:transform .72s cubic-bezier(.76,0,.24,1), visibility 0s linear .72s;
}
.sheet[data-open]{
  transform:none;
  transition:transform .72s cubic-bezier(.76,0,.24,1), visibility 0s;
}
.sheet-top{height:70px}
.sheet-links a{
  border-bottom:0;
  opacity:0; transform:translateY(30px);
  transition:opacity .45s ease, transform .6s cubic-bezier(.22,1,.36,1), color .2s;
}
.sheet[data-open] .sheet-links a{opacity:1; transform:none}
/* the list arrives once the panel is most of the way down */
.sheet[data-open] .sheet-links a:nth-child(1){transition-delay:.22s}
.sheet[data-open] .sheet-links a:nth-child(2){transition-delay:.29s}
.sheet[data-open] .sheet-links a:nth-child(3){transition-delay:.36s}
.sheet[data-open] .sheet-links a:nth-child(4){transition-delay:.43s}
.sheet[data-open] .sheet-links a:nth-child(5){transition-delay:.50s}
.sheet-foot{opacity:0; transform:translateY(18px);
  transition:opacity .45s ease, transform .5s cubic-bezier(.22,1,.36,1)}
.sheet[data-open] .sheet-foot{opacity:1; transform:none; transition-delay:.56s}

/* the chevron flips to point up while the panel is down — the control keeps
   pointing at what it will do next */
.nav-burger svg{transition:transform .42s cubic-bezier(.76,0,.24,1), color .2s}
body.menu-open .nav-burger{position:relative; z-index:40}
body.menu-open .nav-burger svg{transform:rotate(180deg)}

@media (prefers-reduced-motion:reduce){
  .sheet,.sheet-links a,.sheet-foot,.nav-burger svg{transition:none !important}
}

/* The bar does not go away when the menu opens — in the reference it stays
   exactly where it is and the control under your cursor turns into the
   cross. So the nav rides above the panel, and the panel drops its own
   duplicate wordmark and close button. */
body.menu-open nav{z-index:40}
.sheet-top{display:none}
.sheet{padding-top:clamp(76px,9vh,104px); justify-content:flex-start}
.sheet-links a{align-items:flex-start}
/* the one action, as a pill on the same line as the list, not a full-width
   slab across the bottom of the screen */
.sheet-foot{margin-top:clamp(40px,7vh,80px); padding-top:0;
  flex-direction:row; align-items:center; gap:14px}
.sheet-foot .btn{width:auto; min-height:52px; padding:0 30px; border-radius:999px}

/* the two founders — the name carries the weight, the remit sits under it */
.kyn-cols .kyn-role{font-size:clamp(20px,1.9vw,26px); font-weight:500; letter-spacing:-.012em;
  color:var(--ink); margin:0 0 10px}
.kyn-cols .kyn-role + p{max-width:38ch}

/* ── the closing band ─────────────────────────────────────────────────
   This band kept its dark gradient through the flip to white while the
   type tokens went dark with the rest of the site, so the heading was ink
   on near-black and effectively invisible. It stays dark — the page wants
   one anchor at the bottom, and Dixon asked for more contrast, not less —
   and its own type is set light against it.

   Worth recording why the audit missed it: it walks background-color up
   the tree, and this band's background is a gradient, so every element in
   it measured as if it were sitting on white paper and passed. Painted
   backgrounds are a blind spot in that check. */
.closer h2, .closer .kyn-say{color:#F2F6FA}
.closer p, .closer .sub{color:#B7C4D4}
.closer .eyebrow{color:#8FAECB}
.closer .hero-ctas .ulink{color:#DCE5EF; border-bottom-color:rgba(220,229,239,.4)}
.closer .hero-ctas .ulink:hover{color:#FFFFFF; border-bottom-color:#FFFFFF}
.closer .btn{background:#E8EEF5; color:#0B1622}
.closer .btn:hover{background:#FFFFFF; color:#0B1622}
/* The reassurance line under the button, which the hero also carries. Its
   default --faint is a dark slate chosen for white paper; on this band it
   sits at roughly 1.4:1 and disappears. Same blue as the eyebrow. */
.closer .hero-ctas .hero-note{color:#8FAECB}
@media (max-width:640px){
  /* the band goes single-column here, so the note lands under the full-width
     button rather than beside it */
  .closer .hero-ctas .hero-note{justify-self:center; margin-top:14px}
}

/* Four more painted-dark survivors of the flip to white, all of them
   carrying dark type. Same blind spot as the closing band: the audit reads
   background-color, and none of these set one.

   The form fields are the serious one — #0D1217 behind var(--ink) means a
   visitor typing into the only conversion path on the site cannot see what
   they are typing. */
.fld{background:var(--paper); color:var(--ink)}
.fld::placeholder{color:var(--faint)}
.fld:focus{border-color:var(--steel); box-shadow:0 0 0 3px rgba(55,83,110,.12)}
.step.lit .dot{background:var(--steel-wash); box-shadow:0 0 0 4px rgba(55,83,110,.10)}
.portrait{background:linear-gradient(150deg,#E6ECF3 18%,#CDD9E5 58%,#EAEFF5 92%)}
.demo-side{background:var(--card)}
/* and the same floor the Tally sign-in got: under 16px, iOS Safari zooms
   the page the moment a field takes focus, and 42px is under the 44 tap
   minimum Dixon set */
.fld{height:48px; font-size:16px}
textarea.fld{min-height:96px}

/* about: the two founders side by side, no portrait plate to fill */
.who-pair{display:grid; gap:clamp(30px,4vw,64px)}
@media (min-width:800px){.who-pair{grid-template-columns:1fr 1fr}}
/* ── the two founders, with faces ─────────────────────────────────────
   A consultancy whose whole argument is "two people who have done this
   work" should show the two people.

   Circles, not squares. One of the two source files is already a circular
   crop on white, so a square frame would show it as a disc with four white
   corners while the other went edge to edge — two founders photographed to
   two different specs, which is exactly the thing this page cannot afford
   to look like. A circle makes both the same shape whatever arrived.

   height:auto matters more than it looks. The <img> carries width and
   height attributes for layout stability, and an explicit height beats
   aspect-ratio — without this the picture renders 320 wide by 640 tall,
   which is what it was doing.

   Capped at 320px: large enough to read as a person, small enough that the
   name and the evidence under it still lead. */
.who-pair .face{display:block; width:100%; height:auto; max-width:clamp(260px,34vw,480px);
  aspect-ratio:1; object-fit:cover; border-radius:50%;
  background:var(--card); margin:0 0 clamp(18px,2vw,26px)}
@media (max-width:520px){.who-pair .face{max-width:220px}}
/* ── what each founder has actually done ──────────────────────────────
   The two remit paragraphs that used to sit here described the division of
   labour inside DDX — who writes the proposal, who owns security. That is
   an org chart, and a buyer deciding whether these two have sat on his side
   of the desk cannot use it. This is the evidence instead: years, sector,
   systems, employers. Set as a list because a buyer scans it for one line
   that matches his own history and stops there.

   Same hairline treatment as the method list on the home page, so the two
   read as the same kind of statement: a claim you could check. */
/* a founder speaking, not a bio written about him — first person, and set
   at reading size rather than as a caption under a photograph */
/* on the home page the founder blocks are compact, so the line sits closer
   and drops the rule above it — a hairline in a short column reads as a
   divider between two things rather than as a note under one */
.kyn-cols .quirk{margin-top:14px; padding-top:0; border-top:0}

.quirk{font-size:15.5px; line-height:1.55; color:var(--faint); max-width:46ch;
  margin:clamp(14px,1.6vw,20px) 0 0; padding-top:clamp(12px,1.4vw,16px);
  border-top:1px solid var(--line)}
.quirk b{display:block; font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-size:11px; font-weight:500; letter-spacing:.14em; text-transform:uppercase;
  color:var(--steel-dim); margin-bottom:5px}

.who-pair .intro{font-size:17px; line-height:1.62; color:var(--dim); max-width:46ch; margin:0}
.kyn-cols .face + .kyn-role + p{max-width:44ch}

.cv{list-style:none; margin:clamp(14px,1.6vw,20px) 0 0; padding:0;
  border-top:1px solid var(--line)}
.cv li{display:flex; flex-direction:column; gap:2px;
  padding:11px 0; border-bottom:1px solid var(--line)}
.cv b{font-size:15.5px; font-weight:600; letter-spacing:-.008em; color:var(--ink)}
.cv span{font-size:15px; line-height:1.5; color:var(--dim); max-width:44ch}
/* The placeholder shouts until it is replaced. Placeholder text reaches
   production by looking like finished copy, so this one does not: red, mono,
   and it stays that way until somebody deletes the class. */
.cv .todo{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px;
  letter-spacing:.08em; color:var(--red); text-transform:uppercase}
.who-pair h2{font-size:clamp(24px,2.6vw,34px); margin:6px 0 4px}
/* --steel-dim is a border and wash colour — 2.4 on paper, never type */
.role{color:var(--dim); font-weight:500}
.ph{color:var(--dim)}

/* ── tally.html: two whole bands that never made the flip ─────────────
   Both the shader hero and the product demo still painted #0C1118 to
   #0A0E13 while their type had gone dark with the rest of the site, so the
   entire page above the fold was ink on near-black — ten failures, and the
   headline itself at 1.06. They take the page's own surfaces now, and the
   shader that drifts behind the hero is inverted to sit on paper.

   These are the same class of miss as the closing band: painted, not
   coloured, so a background-color audit reads them as white. */
/* Dixon: the Tally page background is too white. Both bands sit a step
   further down the ramp — card to raised rather than white to card — so the
   page has the same tonal range the rest of the site got. */
/* ── the product band, on Tally's own palette ─────────────────────────
   Dixon supplied the brand sheet's six colours:

     #0B0F19  #101827  #1D2638  #5D728E  #E6E6E6  #FFFFFF

   The band is the two mid darks, the light moving over it is the steel, and
   the type is the two lights. Nothing here is picked by eye any more —
   every value on this band is one of those six. */
.shader-zone{background:
  radial-gradient(120% 90% at 76% 10%, rgba(93,114,142,.30), transparent 66%),
  linear-gradient(180deg,#1D2638 0%,#101827 100%)}
.shader-zone .eyebrow{color:#5D728E}
.shader-zone h1, .shader-zone h1 span{color:#FFFFFF}
.shader-zone h1 .accent{color:#E6E6E6}
.shader-zone .sub{color:#E6E6E6}
.shader-zone .btn{background:#FFFFFF; color:#0B0F19}
.shader-zone .btn:hover{background:#E6E6E6; color:#0B0F19}
.shader-zone .ulink{color:#E6E6E6}
.shader-zone .ulink::after{background:#FFFFFF}
.shader-zone{border-bottom-color:rgba(230,230,230,.16)}
.shader-zone #shader.on{opacity:.34; mix-blend-mode:screen}
/* multiply at .30 turned the right half into a grey cloud; the field is
   meant to drift behind the type, not sit on it */
/* superseded — the band is dark again, so the field adds rather than multiplies */
.demo-zone{background:
  radial-gradient(110% 80% at 82% 0%, rgba(55,83,110,.09), transparent 60%),
  linear-gradient(180deg,var(--raised),var(--card))}

/* The reference sets these at reading size, not as small print — a link in
   a column of body copy is a sentence, so it is set like one rather than as
   an 11px caps label. */
.kyn-cols .ulink{font-size:16px; letter-spacing:0; text-transform:none;
  font-weight:500; color:var(--ink) !important}
.kyn-cols .ulink:hover{color:var(--steel) !important}
.ways-head .ways-lede{font-size:17px; line-height:1.62}

/* ── phone type ───────────────────────────────────────────────────────
   Dixon, twice: the words on a phone need to be a lot bigger. Everything
   here had been sized by a clamp whose minimum was set for the desktop
   floor, so a 390pt screen was getting the smallest value in every range —
   a page of captions. On a phone there is one column and one thing to
   read, so it takes the width it has. */
@media (max-width:700px){
  .hero-zone h1.setwide{font-size:clamp(58px,17.2vw,84px); letter-spacing:-.026em}
  /* The home headline is a sentence now, not three words, so it cannot take
     the 17.2vw the caps treatment does — at that size sixty-two characters
     is eleven lines. It gets its own ramp, and it has to be set here rather
     than on .hero-claim alone: the phone rule `.hero h1{font-size:33px}` is
     more specific than a single class and was quietly winning. */
  .hero-zone h1.hero-claim{font-size:clamp(36px,10.4vw,46px); max-width:none}
  .hero-zone .hero .sub{font-size:18px; line-height:1.5; max-width:30ch}
  .kyn-say{max-width:none}
  .kyn-cols .kyn-role{font-size:21px}
  .kyn-cols p{font-size:17px}
  .kyn-label{font-size:12px}
  .ways-head .ways-lede{font-size:17px}
  .way .ttl{font-size:clamp(22px,6vw,28px)}
  .way .desc{font-size:16px; line-height:1.6}
  .closer h2{font-size:clamp(30px,8.4vw,40px)}
  .closer p{font-size:17px}
  .who-pair h2{font-size:clamp(26px,7.4vw,34px)}
}

/* The strip used to sit outside the hero header, so a narrow-width rule
   that hides .hero .works was harmless. It lives inside the header now —
   that rule was switching the tools off on every phone. */
@media (max-width:1000px){
  .hero-zone .works{display:block}
}

/* ── scroll motion ────────────────────────────────────────────────────
   The reference's whole page moves this way: a block rises a little and
   fades up as it comes into reading position, its parts one after another,
   and then it is still. The easing is the same one the menu uses, so the
   site has one motion rather than three. */
.rv{opacity:0; transform:translateY(28px);
  transition:opacity .66s cubic-bezier(.22,1,.36,1) calc(var(--i,0)*80ms),
             transform .78s cubic-bezier(.22,1,.36,1) calc(var(--i,0)*80ms)}
.rv.is-in{opacity:1; transform:none}

/* links: the rule wipes in from the left rather than switching on */
.ulink{position:relative; border-bottom-color:transparent}
.ulink::after{content:""; position:absolute; left:0; right:0; bottom:-1px; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left;
  transition:transform .42s cubic-bezier(.22,1,.36,1)}
.ulink:hover::after, .ulink:focus-visible::after{transform:scaleX(1)}
/* the underline is decoration; the persistent rule stays for anyone with
   motion turned off, or the link loses its affordance entirely */
@media (prefers-reduced-motion:reduce){
  .ulink{border-bottom-color:rgba(55,83,110,.45)}
  .ulink::after{display:none}
  /* The 28px rise is the part that triggers vestibular symptoms; the fade
     is not. So the reveal keeps its opacity transition and its stagger and
     loses only the travel — a visitor who asked for less motion still gets
     the page arriving section by section instead of a flat slab that reads
     as broken. */
  .rv{transform:none;
    transition:opacity .66s cubic-bezier(.22,1,.36,1) calc(var(--i,0)*80ms)}
}

/* the pill fills from the left on the way in, like the reference's */
.pill{position:relative; overflow:hidden; isolation:isolate}
.pill::before{content:""; position:absolute; inset:0; z-index:-1;
  background:var(--steel-wash); transform:scaleX(0); transform-origin:left;
  transition:transform .46s cubic-bezier(.22,1,.36,1)}
.pill:hover::before, .pill:focus-visible::before{transform:scaleX(1)}
.pill:hover{background:transparent}

/* ── the pointer ──────────────────────────────────────────────────────
   Only where a real mouse exists — the script refuses to run otherwise, and
   this hides the native arrow only under the same condition. Text fields
   keep their own cursor: a form you cannot see the caret in is a form
   nobody fills in. */
@media (hover: hover) and (pointer: fine){
  html.has-cursor, html.has-cursor a, html.has-cursor button,
  html.has-cursor [role="button"], html.has-cursor summary, html.has-cursor label{cursor:none}
  html.has-cursor input, html.has-cursor textarea, html.has-cursor select{cursor:auto}
}
.cur-dot, .cur-ring, .cur-pop{
  position:fixed; left:0; top:0; z-index:60; pointer-events:none;
  border-radius:999px; opacity:0; will-change:transform;
}
.cur-dot{width:6px; height:6px; background:var(--ink);
  transition:opacity .2s ease, width .28s cubic-bezier(.22,1,.36,1),
             height .28s cubic-bezier(.22,1,.36,1)}
.cur-ring{width:30px; height:30px; border:1px solid var(--steel);
  transition:opacity .25s ease, width .34s cubic-bezier(.22,1,.36,1),
             height .34s cubic-bezier(.22,1,.36,1),
             background-color .3s ease, border-color .3s ease}
body.cur-on .cur-dot{opacity:1}
body.cur-on .cur-ring{opacity:.75}

/* over anything pressable the ring opens and fills, and the dot goes inside */
body.cur-hot .cur-ring{width:56px; height:56px; background:rgba(55,83,110,.10); border-color:var(--steel)}
body.cur-hot .cur-dot{width:0; height:0}
/* pressing collapses it */
body.cur-down .cur-ring{width:22px; height:22px; background:rgba(55,83,110,.22)}
body.cur-down.cur-hot .cur-ring{width:44px; height:44px}

/* and the release sends one out */
.cur-pop{width:26px; height:26px; margin:-13px 0 0 -13px;
  border:1px solid var(--steel); opacity:.55;
  animation:cur-pop .52s cubic-bezier(.22,1,.36,1) forwards}
@keyframes cur-pop{
  from{transform:scale(.4); opacity:.55}
  to{transform:scale(2.6); opacity:0}
}
/* the declared opacity loses to the keyframe, so the firmer touch ring
   needs its own set of frames rather than its own opacity */
@keyframes cur-pop-touch{
  from{transform:scale(.35); opacity:.8}
  to{transform:scale(2.9); opacity:0}
}
@media (prefers-reduced-motion:reduce){
  .cur-dot,.cur-ring,.cur-pop{display:none}
}

/* ── pressing, on a phone ─────────────────────────────────────────────
   No hover exists there, so the hover states the desktop gets never fire.
   These are the same moves at press time: the target gives a little, and
   the ring that the desktop cursor leaves on click is left at the point
   the finger touched instead. */
@media (hover: none){
  .btn, .pill, .nav-cta, .way, .sheet-links a, .ulink{
    transition:transform .16s cubic-bezier(.22,1,.36,1),
               background-color .18s ease, color .18s ease, border-color .18s ease}
  .btn.is-press, .nav-cta.is-press{transform:scale(.965); background:var(--steel-hi)}
  .pill.is-press{transform:scale(.965); background:var(--steel-wash)}
  .way.is-press{background:var(--card); padding-left:14px}
  .way.is-press .arw{background:var(--steel); border-color:var(--steel); color:#FFFFFF}
  .sheet-links a.is-press .st{color:var(--steel)}
  .ulink.is-press{color:var(--steel)}
  /* The ring the finger leaves behind. Bigger and firmer than the desktop
     one — a fingertip covers what it touches, so the acknowledgement has to
     clear the finger to be seen at all. */
  .cur-pop{display:block; width:40px; height:40px; margin:-20px 0 0 -20px;
    border-width:1.5px; animation-name:cur-pop-touch}
}

/* every control answers a press, including the ones whose only desktop
   state was a hover colour */
@media (hover: none){
  .btn.is-press, .nav-cta.is-press, .pill.is-press, .btn-ghost.is-press,
  .way.is-press, .ulink.is-press, .sheet-links a.is-press, .hix.is-press,
  .brand.is-press, .nav-burger.is-press{transition-duration:.12s}
  .btn-ghost.is-press{border-color:var(--steel); background:var(--steel-wash)}
  .brand.is-press{opacity:.72}
  .nav-burger.is-press{color:var(--steel)}
  .hix.is-press{background:var(--card)}
}

/* The scrim over this band was a stack of white washes, shaped to keep dark
   type off the bright drifts of the shader. The band is dark now and the
   type is reversed out of it, so every one of those washes was working
   against the thing it was protecting — the left third came back almost
   grey. It goes the other way: a soft darkening under the copy, nothing
   over the open right-hand side where the light is the point. */
/* the scrim keeps the drifts off the copy; it does not need to swallow the
   left half of the band to do it */
.shader-zone::after{background:
  linear-gradient(90deg, rgba(11,22,34,.26), rgba(11,22,34,.08) 46%, transparent 70%) !important}
@media (max-width:820px){
  .shader-zone::after{background:
    linear-gradient(180deg, rgba(11,22,34,.22) 0%, rgba(11,22,34,.38) 44%, rgba(11,22,34,.5) 100%) !important}
  .shader-zone #shader.on{opacity:.5}
}

/* ── one type scale, every page ───────────────────────────────────────
   Dixon: follow the home page's text style across all the pages. Each page
   had grown its own heading sizes — the product page at clamp(28,4.2vw,46),
   the closing band at clamp(30,4.4vw,46), the section heads at
   clamp(30,4.3vw,62) — so the same kind of statement was three different
   sizes depending on which page you were reading it on. One statement size,
   one body size, one label, set here and inherited everywhere. */
/* Sizes only. This rule sets the scale for every page and is deliberately
   silent about colour: it comes after the reversed-out headings on the dark
   bands, and a colour here puts ink back on near-black. */
h2, .kyn-say, .sec h2, .demo-h, .closer h2, .subhero h2{
  /* `.sec h2` is in the list because line 382 sets it to clamp(26,3.4vw,38)
     with two selectors, and it was quietly winning over every heading inside
     a .sec — which is most of them. Its margin and measure still come from
     there; only the size is settled here.

     62 to 88. Every heading on this site is a sentence somebody would say
     out loud rather than a label, so they are set at headline scale. This
     rule is the only place the size lives — an earlier attempt to raise it
     on .kyn-say alone was dead code, because this comes later and matches
     with the same weight. */
  font-size:clamp(34px,5.8vw,88px);
  font-weight:400;
  line-height:1.06;
  letter-spacing:-.028em;
}
/* names and card titles are not statements — they keep their own step */
.who-pair h2, .kyn-cols h3, .kyn-cols .kyn-role, .way .ttl{
  font-size:clamp(20px,1.9vw,26px); font-weight:500; line-height:1.25;
  letter-spacing:-.012em;
}
.demo-lede, .closer p, .ways-lede, .kyn-cols p, .who-pair p, .sub{
  font-size:17px; line-height:1.62;
}
.eyebrow, .kyn-label, .k{
  font-size:11.5px; font-weight:500; letter-spacing:.16em; text-transform:uppercase;
}
@media (max-width:700px){
  h2, .kyn-say, .demo-h, .closer h2, .subhero h2{font-size:clamp(34px,10.4vw,54px); line-height:1.1}
  .who-pair h2, .kyn-cols h3, .kyn-cols .kyn-role, .way .ttl{font-size:21px}
  .demo-lede, .closer p, .ways-lede, .kyn-cols p, .who-pair p, .sub{font-size:17px}
}

/* ── the phone landing ────────────────────────────────────────────────
   Dixon, from a real handset: the phone landing is quite poor. It was
   bottom-aligned, so the first two thirds of the screen were empty field
   and the promise sat down by the thumb — the opposite of the desktop,
   where it is the middle of the page. Centred, with the field carrying the
   full height behind it. */
@media (max-width:700px){
  /* svh first, but an engine that does not know the unit throws the whole
     declaration away and falls back to the min-height:0 further up — which
     is what left a band of empty white under the strip on Dixon's handset.
     The vh line lands first so there is always a height to fall back to. */
  .hero-zone{align-items:center; min-height:100vh; min-height:100svh}
  .hero-zone .hero{padding-top:78px; padding-bottom:26px}
  .hero-zone .hero-say{padding:0 4px}
  .hero-zone .hero .sub{margin-top:20px}
  .hero-zone .hero-ctas{margin-top:26px}
  .hero-zone .works{margin-top:26px}
}

/* The product page's hero headline was 62px against the home page's 132 —
   the same kind of statement, in the same place, at half the size. Dixon
   spotted it as the text style being different. One hero scale. */
.subhero h1.setwide{
  /* the home page's hero runs to 132px on a screen with nothing above it;
     this one has a three-line statement inside a fixed band, and at 132 the
     first line went up behind the nav. Same face, same tracking, same case
     — one step down in size so it fits the room it is in. */
  font-size:clamp(40px,7vw,96px);
  letter-spacing:-.016em;
  line-height:1.02;
}
@media (max-width:700px){
  .subhero h1.setwide{font-size:clamp(46px,12.6vw,64px); letter-spacing:-.02em; line-height:1.04}
}

/* ── the primary button, under the pointer ────────────────────────────
   It only swapped one navy for another, which at 46px reads as nothing
   happening. Now a fill grows out of a dot at the left edge and takes the
   whole button, the label steps aside to let it through, and an arrow
   arrives at the end. Transform and opacity only, so it stays on the
   compositor. */
.btn{position:relative; overflow:hidden; isolation:isolate;
  transition:background .18s ease, color .18s ease, box-shadow .3s ease}
.btn::before{content:""; position:absolute; z-index:-1;
  left:20px; top:50%; width:10px; height:10px; border-radius:999px;
  background:var(--steel-hi);
  transform:translateY(-50%) scale(0);
  transition:transform .48s cubic-bezier(.22,1,.36,1)}
.btn:hover::before, .btn:focus-visible::before{transform:translateY(-50%) scale(42)}
.btn .btn-arw{display:inline-block; width:0; overflow:hidden; opacity:0;
  margin-left:0; transform:translateX(-6px);
  transition:width .4s cubic-bezier(.22,1,.36,1), opacity .3s ease,
             transform .4s cubic-bezier(.22,1,.36,1), margin-left .4s cubic-bezier(.22,1,.36,1)}
.btn:hover .btn-arw, .btn:focus-visible .btn-arw{width:1em; opacity:1; margin-left:10px; transform:none}
.btn:hover{box-shadow:0 10px 26px rgba(42,67,88,.22)}

/* the light buttons on the two dark bands grow a dark fill instead */
.closer .btn::before, .shader-zone .btn::before{background:#FFFFFF}
.closer .btn:hover, .shader-zone .btn:hover{box-shadow:0 10px 26px rgba(0,0,0,.28)}

@media (prefers-reduced-motion:reduce){
  .btn::before{transition:none}
  .btn .btn-arw{display:none}
}
@media (hover: none){
  /* no hover on a phone — the press state already answers, and a fill that
     never retracts would leave the button stuck in its hover colour */
  .btn::before{display:none}
  .btn .btn-arw{display:none}
}

/* and the section under the fold starts sooner on a phone — a full screen of
   hero followed by 90px of nothing reads as the page having stopped */
@media (max-width:700px){
  .kyn .sec, section .sec{padding-top:44px; padding-bottom:48px}
  .hero-zone .works{margin-bottom:0}
}

/* The empty white band under the strip was not empty layout — it was the
   veil, which fades to 98% white from three quarters of the way down. That
   was shaped for a hero whose copy ran to the bottom of the screen; the copy
   is centred now, so the wash was covering live field. It fades far less,
   and far lower. */
@media (max-width:700px){
  .hero-zone .veil{background:linear-gradient(180deg,
    rgba(255,255,255,0) 0%, rgba(255,255,255,0) 62%,
    rgba(255,255,255,.18) 84%, rgba(255,255,255,.42) 100%) !important}
}

/* Tally's mark, on its own chip. The old lockup drew four tally strokes and
   a slash; the new one is the slab T with the play triangle, so the chip
   just holds it and the geometry does the work. */
/* .lockup .tile paints a light chip, and it is more specific than a bare
   .tile-tally — the white mark was disappearing into it */
.lockup .tile-tally{background:#101C33; border-color:#101C33; border-radius:10px}
.tile-tally svg{display:block}

/* The reference's video background was built and pulled at Dixon's word —
   it is in the history if it is ever wanted. The band is the brand steel
   with our own shader drifting over it. */
/* One rule for this, at the end of the file, so the eight earlier ones
   cannot be mistaken for the live setting. .34 is where the band stays the
   surface and the drift stays a drift across it. */
.shader-zone #shader.on{opacity:.34; mix-blend-mode:screen}

/* The wordmark as the brand sheet sets it: TALLY in wide-tracked caps, and
   the line under it is the tagline in the brand blue — not a "by" line. */
/* the wordmark is artwork, not type — its A has no crossbar, which no font
   will give you */
.lockup .wm-svg{display:block; height:26px; width:auto; color:var(--ink)}
.lockup .by{display:block; margin-top:6px; text-transform:uppercase;
  letter-spacing:.16em; font-size:9.5px; font-weight:600; color:#2F6FE0}

/* the pitch bullets carry a lead phrase now, so the phrase has to be the
   thing you see when you skim */
.bullets b{font-weight:500; color:var(--ink)}
.bullets{gap:14px}

/* The full lockup as the sheet draws it: mark, a hairline between, then the
   wordmark with the tagline set under it to the same width. */
.lockup{align-items:center}
.lockup-rule{display:block; width:1px; align-self:stretch; min-height:38px;
  background:var(--line-strong); margin:0 4px}
.lockup .by{display:block; margin-top:7px; text-transform:uppercase;
  font-size:9px; font-weight:600; color:#1F6BFF;
  /* the sheet sets the tagline to the width of the word above it */
  letter-spacing:.19em}

/* the contact column, on the same scale as every other section head */
.contact-say .kyn-say{max-width:min(720px,100%)}
.contact-lede{font-size:17px; line-height:1.62; color:var(--dim); margin:22px 0 0; max-width:44ch}
.contact-note{font-size:15px; color:var(--faint); margin:16px 0 0}
.contact-mail{font-size:16px; margin:26px 0 0}
.contact-mail a{font-weight:500; border-bottom:1px solid var(--line-strong); padding-bottom:2px}
.contact-mail a:hover{border-bottom-color:var(--steel)}

/* The third line of the headline is the accent; with --steel now the same
   deep navy as the ink it had stopped being one. #5D728E is the sheet's
   only mid tone and measures 4.64 on paper, which clears the 3:1 large
   text needs with room. */
.hero-zone h1.setwide .accent{color:#5D728E}

/* ── the 12px phone floor, made true ──────────────────────────────────
   The README has always said no phone type sits below 12px; a sweep of
   real renders found a dozen labels that did. This block is the floor —
   every mono cap and micro label at its smallest permitted size on a
   phone, letter-spacing eased where twelve pixels of tracked caps would
   otherwise overrun a 320px column. */
@media (max-width:640px){
  .eyebrow,.kyn-label{font-size:12px}
  .systems-cap{font-size:12px;letter-spacing:.12em}
  .quirk b{font-size:12px}
  .pill{font-size:12px}
  .by{font-size:12px}
  .demo-live,.demo-k,.demo-wait,.demo-side b{font-size:12px}
  /* the hero note, unhidden: centred under the full-width button */
  .hero-zone .hero-ctas .hero-note{margin-top:12px;justify-self:center;text-align:center}
}

/* The lockup strap needs the compound selector — the 9px base rule is
   .lockup .by, and a bare .by can never outrank it however late it comes.
   A rule to this effect exists mid-file and measures as inert; this one,
   last in the cascade, actually lands. */
@media (max-width:820px){
  .lockup .by{font-size:12px;letter-spacing:.14em}
}

/* ── 320px ────────────────────────────────────────────────────────────
   The nav is the one thing that never wraps: gutters, brand, burger and
   the Contact button add to 352px at the 640 sizes, and an SE-width
   screen gets a sideways scroll. Everything gives a little; nothing
   drops below a 44px tap. */
@media (max-width:360px){
  .nav-in{padding-left:14px;padding-right:14px}
  .brand{gap:8px}
  .brand .ddx-word{width:80px}
  .nav-burger{width:44px}
  .nav-cta{padding:0 12px;letter-spacing:.08em}
  .dock{padding-left:14px;padding-right:14px}
  .dock-note{display:none}
  .systems-cap{letter-spacing:.1em}
  /* The real 320px culprit was never the nav: once anything nudges past
     the device width, the mobile layout viewport widens to fit it, every
     inset:0 and width:100% element follows the wider viewport, and the
     expansion sustains itself. clip severs the loop — content wider than
     the device is cropped instead of becoming the new page width. clip,
     not hidden: hidden would make body a scroll container. */
  html,body{overflow-x:clip}
  .sheet{overflow-x:hidden}
  .sheet-links a{font-size:30px}
  /* the logo strip's 94vw sits 3px proud at this width — the clip hides
     it, but the border-top should end where the screen does */
  .systems{width:calc(100% - 28px)}
}

/* ── B1: the three beats, side by side ────────────────────────────────
   Stacked, the triad ran two screens and read as three sections; across
   one row it reads as one system, which is what the hero's three-word
   line promises. Each column carries the lockup's own geometry at small
   size — drawn open for the stage still looking, folded for the one
   building, filled for the one that runs — the same family as the hero
   facets, so nothing here is a picked icon. The phone keeps the stack. */
.smark{width:17px;height:17px;color:var(--steel);flex-shrink:0}
.stage .n{display:inline-flex;align-items:center;gap:9px}
@media (min-width:821px){
  .ways-in{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
    gap:0 clamp(26px,3.2vw,48px);align-items:start}
  .ways-in > .ways-head{grid-column:1/-1}
  /* undoes the two-column number-gutter layout the stacked stages used */
  .ways-in > .stage{display:block;padding-top:26px}
  .ways-in > .stage .n{padding-top:0;margin-bottom:16px}
  .ways-in > .stage h3{margin-top:0;font-size:clamp(22px,2vw,28px)}
  .ways-in > .stage p{font-size:16px}
}

/* "Where we excel" — redesigned 2026-08-25.
   Was five cards in a three-column grid, which orphans a cell, in boxes still
   padded for the descriptions cut on 08-23. Now the names ARE the section, so
   they get the size to be it: no cards, no fills, rules only. The eyebrow sits
   above the heading rather than in the 200px label column, which held two words
   and wasted a fifth of the width — so this section opts out of .kyn-in. */
.excel-head{display:flex; align-items:baseline; gap:clamp(14px,2vw,28px);
  flex-wrap:wrap; margin-bottom:clamp(32px,4vw,48px)}
.excel-head .kyn-label{margin:0; flex:none}
.excel-head .kyn-say{margin:0}

.excel-list{list-style:none; counter-reset:cap; margin:0; padding:0;
  border-top:1px solid var(--line)}
.excel-list li{counter-increment:cap; display:flex; align-items:baseline;
  justify-content:space-between; gap:clamp(16px,3vw,32px);
  padding:clamp(20px,2.4vw,30px) 0; border-bottom:1px solid var(--line)}
/* 44px at the top end: large enough to be the object on the screen, and the
   largest that keeps "Workflow automation" on one line down to 390px once it
   scales. Weight 500 rather than 600 — at this size the heavier cut shouts. */
.excel-list b{font-size:clamp(27px,3.6vw,44px); font-weight:500;
  letter-spacing:-.03em; line-height:1.05; color:var(--ink); text-wrap:balance}
/* The counter is a landmark, not a label: --line-strong keeps it quiet enough
   not to compete with the name beside it. */
.excel-list li::after{content:counter(cap,decimal-leading-zero);
  font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:12px;
  font-weight:500; letter-spacing:.08em; color:var(--line-strong); flex:none}
@media (max-width:640px){
  .excel-list li::after{font-size:11px}
}

/* ── the questionnaire speaks up ──────────────────────────────────────
   The questions were set as 9.5px tracked mono caps — chrome, when they
   are the actual questions a visitor is being asked to answer. They are
   sentences now, at reading size, in the site's own text face; the mono
   caps stay on the frame (step label, the note) where chrome belongs. */
/* ── when to meet ──────────────────────────────────────────────────────
   A month grid and a column of slots, in this site's own vocabulary.

   Built here rather than installed. The pattern came from a React component
   library, and taking that literally would have meant a build pipeline, a
   component library and a second set of colour tokens on a page that is
   static HTML - for one optional field, arriving in somebody else's visual
   language. The interaction is the part worth copying; the stack is not.

   Every colour below is an existing variable, so it follows the rest of the
   form when the palette moves. */
.meet{margin-top:clamp(18px,2.2vw,26px); padding-top:16px;
  border-top:1px solid var(--line)}
.meet-head{display:flex; flex-wrap:wrap; align-items:baseline;
  justify-content:space-between; gap:10px}
.meet-picked{font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-size:11px; letter-spacing:.04em; color:var(--dim)}

.meet-body{display:grid; gap:14px; margin-top:12px}
@media (min-width:620px){.meet-body{grid-template-columns:minmax(0,1fr) 132px}}

.meet-cal,.meet-slots{border:1px solid var(--line); border-radius:2px;
  background:var(--card); padding:10px}

.meet-nav{display:flex; align-items:center; justify-content:space-between;
  margin-bottom:8px}
.meet-month{font-size:13px; font-weight:500; color:var(--ink)}
.meet-arrow{width:26px; height:26px; line-height:1; border:1px solid var(--line);
  border-radius:2px; background:transparent; color:var(--dim); cursor:pointer;
  transition:border-color .15s, color .15s}
.meet-arrow:hover:not(:disabled){border-color:var(--steel); color:var(--ink)}
.meet-arrow:disabled{opacity:.32; cursor:default}

.meet-dow,.meet-grid{display:grid; grid-template-columns:repeat(7,1fr); gap:2px}
.meet-dow{margin-bottom:4px}
.meet-dow span{text-align:center; font-size:10px; letter-spacing:.08em;
  color:var(--line-strong)}

.meet-day{aspect-ratio:1; min-height:30px; border:1px solid transparent;
  border-radius:2px; background:transparent; color:var(--ink);
  font-size:12.5px; cursor:pointer;
  transition:background-color .12s, border-color .12s, color .12s}
.meet-day:hover:not(:disabled){border-color:var(--steel)}
/* Disabled days stay visible rather than vanishing: a grid with holes in it
   is harder to read than one that shows which days are simply not on offer. */
.meet-day:disabled{color:var(--line-strong); cursor:default}
.meet-day.on{background:var(--ink); border-color:var(--ink); color:#fff}

.meet-slots-h{display:block; margin-bottom:8px; font-size:10px;
  letter-spacing:.08em; text-transform:uppercase; color:var(--line-strong)}
/* Capped and scrollable, so fourteen slots do not make the calendar beside
   them twice as tall as it needs to be. */
.meet-slotlist{display:grid; gap:4px; max-height:196px; overflow-y:auto;
  padding-right:2px}
.meet-slot{padding:6px 8px; border:1px solid var(--line); border-radius:2px;
  background:transparent; color:var(--ink);
  font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:11.5px;
  cursor:pointer; transition:background-color .12s, border-color .12s, color .12s}
.meet-slot:hover:not(:disabled){border-color:var(--steel)}
.meet-slot:disabled{color:var(--line-strong); cursor:default}
.meet-slot.on{background:var(--ink); border-color:var(--ink); color:#fff}

.meet-note{margin:10px 0 0; font-size:11.5px; color:var(--dim)}

/* Newsletter consent on the enquiry form. Deliberately quiet: it is an
   optional extra at the end of a form somebody is already filling in, not a
   second call to action competing with Send. The box must never ship
   pre-ticked — a pre-ticked box is not consent under PECR, and every address
   collected that way would be unusable. */
.optin{margin-top:clamp(18px,2.2vw,26px);padding-top:16px;
  border-top:1px solid var(--line)}
.optin label{display:flex;align-items:flex-start;gap:11px;cursor:pointer}
.optin input{margin:2px 0 0;width:17px;height:17px;flex-shrink:0;
  accent-color:var(--steel);cursor:pointer}
.optin span{font-size:14px;line-height:1.55;color:var(--dim)}
.optin label:hover span{color:var(--ink)}
.optin input:focus-visible{outline:2px solid var(--steel);outline-offset:2px}

.lbl{font:500 15px/1.45 "Switzer",system-ui,sans-serif;
  letter-spacing:0;text-transform:none;color:var(--ink);margin-bottom:7px}
.pick{font-size:14px}
.wizfoot .note{font-size:12px}
.form-card .rcard-head span{font-size:11px}
@media (max-width:640px){
  /* outranks the old 12px phone bump for .lbl further up */
  .lbl{font-size:15px;letter-spacing:0}
}

/* ── the four-column footer ───────────────────────────────────────────
   The one-line footer said only what the site is called. This one says
   what we do, where the pages are and how to reach us — the reference's
   structure in this site's own language: hairlines, mono column heads,
   no gradients, and no social accounts we do not have. */
footer .foot-grid{max-width:1160px;margin:0 auto;
  padding:clamp(40px,5vw,64px) 32px clamp(28px,3.4vw,44px);
  display:grid;gap:clamp(28px,4vw,44px)}
@media (min-width:820px){
  footer .foot-grid{grid-template-columns:1.5fr 1fr 1fr 1fr}
}
.foot-brand .brand{display:inline-flex}
.foot-brand p{font-size:14.5px;line-height:1.6;color:var(--dim);
  margin:16px 0 0;max-width:30ch}
.foot-h{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;
  font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  color:var(--steel-dim);margin:0 0 12px}
.foot-col a{display:block;font-size:14px;line-height:1.4;color:var(--dim);
  padding:5px 0;transition:color .15s ease}
.foot-col a:hover{color:var(--ink)}
/* ── where we excel: five cards on a three-column grid ──────────────
   Dixon's reference, 6 September 2026. Paper cards on the section's
   ground, one lime, one dark; number and icon at the corners, the name as
   the object, one sentence under it. The two empty cells are deliberate. */
.excel-grid{display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); grid-auto-flow:dense; gap:6px; margin-top:clamp(28px,4vw,48px); background:var(--raised); padding:6px}
.xcard{position:relative; display:flex; flex-direction:column; justify-content:flex-end; min-height:clamp(220px,24vw,300px); padding:clamp(18px,2.4vw,32px);
  background:var(--paper); color:var(--ink); text-decoration:none; border:1px solid var(--line); transition:transform .18s ease, box-shadow .18s ease}
.xcard:hover{transform:translateY(-2px); box-shadow:0 10px 30px rgba(11,15,25,.08)}
.xcard.span2{grid-column:span 2}
.xcard.lime{background:var(--steel); border-color:var(--steel); color:#F2F6FA}
.xcard.lime p{color:#D9E2EC}
.xcard.dark{background:var(--ink); border-color:var(--ink); color:#F2F6FA}
.xnum{position:absolute; top:clamp(14px,1.8vw,22px); left:clamp(18px,2.4vw,32px); font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:10.5px; letter-spacing:.12em; opacity:.6}
.xicon{position:absolute; top:clamp(14px,1.8vw,22px); right:clamp(18px,2.4vw,32px); width:20px; height:20px; opacity:.7}
.xcard h3{margin:0 0 10px; font-size:clamp(22px,2.2vw,30px); font-weight:500; letter-spacing:-.025em; line-height:1.1; white-space:nowrap}
.xcard.span2 h3{font-size:clamp(26px,2.8vw,36px)}
@media (max-width:560px){.xcard h3{white-space:normal}}
.xcard p{margin:0; font-size:14.5px; line-height:1.55; max-width:40ch; opacity:.85}
.xcard.dark p{color:#D9E2EC}
.xgo{position:absolute; right:clamp(18px,2.4vw,32px); bottom:clamp(14px,1.8vw,22px); font-size:14px; opacity:.55}
@media (max-width:900px){
  .excel-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .xcard.span2{grid-column:span 2}
}
@media (max-width:560px){
  .excel-grid{grid-template-columns:1fr}
  .xcard.span2{grid-column:auto}
  .xcard{min-height:200px}
}

/* ── value check: three sliders, one number ──────────────────────────
   Two columns like the rest of the page's "say + show" bands; the result
   panel is the one dark surface in the section, so the number is the
   thing the eye lands on. The slider track is painted with a custom
   property so the filled part reads as the value without a library. */
.value-in{display:grid; grid-template-columns:minmax(0,.7fr) minmax(0,1.7fr); gap:clamp(28px,4vw,64px); align-items:center}
.value-lede{font-size:17px; line-height:1.6; color:var(--dim); margin:22px 0 0; max-width:44ch}
.value-card{display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); border:1px solid var(--line); background:var(--paper); min-width:0; min-height:clamp(420px,42vw,600px)}
.value-inputs{padding:clamp(28px,4vw,60px); display:flex; flex-direction:column; justify-content:center}
.vrow{display:grid; grid-template-columns:1fr auto; row-gap:14px; padding:22px 0 28px; border-bottom:1px solid var(--line); cursor:pointer}
.vrow:last-child{border-bottom:0}
.vlbl{font-size:clamp(15px,1.3vw,18px); color:var(--dim)}
.vval{font-size:clamp(18px,1.6vw,24px); font-weight:600; color:var(--ink); text-align:right}
.vrow input[type=range]{grid-column:1 / -1; width:100%; margin:0; height:28px; background:transparent; -webkit-appearance:none; appearance:none; --pct:0%}
.vrow input[type=range]::-webkit-slider-runnable-track{height:2px; border-radius:2px;
  background:linear-gradient(to right, var(--steel) 0 var(--pct), var(--line-strong) var(--pct) 100%)}
.vrow input[type=range]::-moz-range-track{height:2px; border-radius:2px; background:var(--line-strong)}
.vrow input[type=range]::-moz-range-progress{height:2px; background:var(--steel)}
.vrow input[type=range]::-webkit-slider-thumb{-webkit-appearance:none; width:22px; height:22px; margin-top:-10px; border-radius:50%;
  background:var(--ink); border:4px solid var(--paper); box-shadow:0 0 0 1px var(--ink); cursor:pointer}
.vrow input[type=range]::-moz-range-thumb{width:12px; height:12px; border-radius:50%; background:var(--ink); border:4px solid var(--paper); box-shadow:0 0 0 1px var(--ink); cursor:pointer}
.vrow input[type=range]:focus-visible{outline:2px solid var(--steel); outline-offset:6px}
.value-out{min-width:0; background:var(--steel); color:#F2F6FA; padding:clamp(28px,4vw,60px) clamp(24px,3.4vw,52px); display:flex; flex-direction:column; justify-content:center}
.vout-label{color:#8FAECB; margin:0 0 18px; font-size:12.5px}
.vout-big{margin:0; font-weight:500; letter-spacing:-.03em; line-height:1; font-size:clamp(44px,5vw,84px); color:#FFF; display:flex; flex-wrap:nowrap; align-items:baseline; gap:0 10px; min-width:0; white-space:nowrap}
.vout-big small{font-size:19px; font-weight:400; letter-spacing:0; margin-left:0; color:#D9E2EC}
.vout-line{display:flex; flex-direction:column; align-items:flex-start; gap:6px; margin-top:34px; padding-top:22px; border-top:1px solid rgba(242,246,250,.22); font-size:15px; color:#D9E2EC}
.vout-line span{white-space:nowrap}
.vout-line b{font-size:clamp(24px,2.4vw,32px); font-weight:600; color:#FFF}
.vout-note{margin:24px 0 0; font-size:14px; line-height:1.55; color:#B7C6D6}
@media (max-width:1000px){
  .value-in{grid-template-columns:1fr; gap:26px}
  .value-card{grid-template-columns:1fr}
}

.foot-note{font-size:12.5px;color:var(--faint);margin:6px 0}
.foot-base{border-top:1px solid var(--line);padding:18px 32px;
  text-align:center;font-size:11.5px;letter-spacing:.04em;color:var(--faint)}
@media (max-width:640px){
  footer .foot-grid{padding-left:20px;padding-right:20px}
  .foot-base{padding-left:20px;padding-right:20px}
  /* the tap targets the one-line footer already promised */
  .foot-col a{min-height:40px;display:flex;align-items:center;padding:0}
}


/* ── who we are: the four claims become the site's cards ──────────────
   The excel grid's language — numbered, hairline, a lift on hover — so
   the about page stops being four grey boxes in a row. */
.who-grid{counter-reset:wc}
.wcard{counter-increment:wc;
  transition:transform .2s ease,border-color .2s ease,box-shadow .2s ease}
.wcard::before{content:counter(wc,decimal-leading-zero);display:block;
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:12px;
  font-weight:500;letter-spacing:.14em;color:var(--steel-dim);margin-bottom:10px}
.wcard:hover{transform:translateY(-2px);border-color:var(--steel);
  box-shadow:0 12px 30px -20px rgba(11,15,25,.4)}
@media (prefers-reduced-motion:reduce){.wcard:hover{transform:none}}

/* the co-founder tag reads as a mark, not a stray word */
.who-pair .k{display:inline-flex;align-items:center;height:24px;padding:0 11px;
  margin-top:18px;border:1px solid rgba(107,139,164,.45);border-radius:999px;
  font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:11px;
  font-weight:500;letter-spacing:.14em;text-transform:uppercase;color:var(--steel)}

/* ── contact: what happens next ───────────────────────────────────────
   The left column said only "tell us" — now it walks the three steps a
   visitor is actually agreeing to. Reuses the method list; the spine
   track belongs to the approach section, so it is switched off here. */

/* ── the approach, said beside the steps ──────────────────────────────
   Dixon's call: the statement on the right, the five steps on the left.
   The heading rides sticky so the claim stays on screen while the spine
   fills and the numbers light — the section argues with itself in view.
   The phone keeps the stack. */
@media (min-width:900px){
  #approach .kyn-body{display:grid;
    grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
    gap:clamp(36px,5vw,80px);align-items:start}
  #approach .kyn-body .kyn-say{order:2;position:sticky;top:110px}
  #approach .kyn-body .method{order:1;margin-top:6px;max-width:none}
}

/* the statement sits beside the list now, in a half-width column — at the
   full display clamp it overflowed its own section */
@media (min-width:900px){
  #approach .kyn-body .kyn-say{font-size:clamp(30px,3.1vw,46px);line-height:1.12}
}

/* ── the nav's one button finally answers the pointer ─────────────────
   Every other control on the site lifts or fills on hover; the Contact
   pill did nothing. Same gesture as the rest: a small rise, a deeper
   ink, and the press-down the buttons already share. */
.nav-cta{transition:background-color .2s ease,transform .2s ease,box-shadow .2s ease}
.nav-cta:hover{background:var(--ink);transform:translateY(-1px);
  box-shadow:0 10px 22px -12px rgba(11,22,34,.55)}
.nav-cta:active{transform:translateY(0) scale(.97)}
@media (prefers-reduced-motion:reduce){
  .nav-cta:hover,.nav-cta:active{transform:none}
}

/* the steps stopped holding their side of the section once the statement
   moved beside them — step titles at reading size against a display-size
   claim read as a caption. They carry display weight of their own now. */
@media (min-width:900px){
  #approach .method li{grid-template-columns:3.2em 1fr;
    padding:clamp(18px,2.1vw,28px) 0;gap:6px clamp(14px,1.6vw,24px)}
  #approach .method li::before{font-size:13px;padding-top:.6em}
  #approach .method b{font-size:clamp(23px,2.1vw,31px);letter-spacing:-.018em}
  #approach .method span{font-size:17px;line-height:1.55;max-width:none}
}


/* ── the menu button looks like a button ──────────────────────────────
   A bare chevron floating in the middle of the bar read as something
   broken rather than the way in. It gets the site's control shape — a
   hairline ring, a hover, and the chevron turns over while the menu is
   open so the same button reads as the way back out. */
.nav-burger{border:1px solid var(--line-strong);border-radius:999px;
  color:var(--dim);
  transition:border-color .2s ease,background-color .2s ease,color .2s ease}
.nav-burger:hover{border-color:var(--steel);background:var(--steel-wash);color:var(--steel)}
.nav-burger svg{transition:transform .28s cubic-bezier(.22,1,.36,1)}
.nav-burger[aria-expanded="true"] svg{transform:rotate(180deg)}
@media (prefers-reduced-motion:reduce){.nav-burger svg{transition:none}}

/* ── the inner pages arrive, instead of appearing ─────────────────────
   The sub-page heroes had no entrance while the home hero staged every
   line — which is most of why about and contact read as flat. Same
   rise, same order: eyebrow, claim, headline line by line, then the
   rest. Decoration only; reduced-motion gets the finished page. */
@media (prefers-reduced-motion:no-preference){
  .subhero .eyebrow,.subhero-claim,.subhero h1.setwide span,
  .subhero .sub,.subhero .hero-ctas{
    opacity:0;animation:riseIn .8s cubic-bezier(.16,.84,.44,1) forwards}
  .subhero .eyebrow{animation-delay:.08s}
  .subhero-claim{animation-delay:.16s}
  .subhero h1.setwide span:nth-child(1){animation-delay:.24s}
  .subhero h1.setwide span:nth-child(2){animation-delay:.34s}
  .subhero h1.setwide span:nth-child(3){animation-delay:.44s}
  .subhero .sub{animation-delay:.52s}
  .subhero .hero-ctas{animation-delay:.62s}

  /* the founders' faces answer the pointer: slightly quiet until met */
  .face{filter:grayscale(30%);
    transition:filter .45s ease,transform .45s cubic-bezier(.22,1,.36,1)}
  .face:hover{filter:grayscale(0);transform:scale(1.015)}

  /* the form and its chips respond like the rest of the site's controls */
  .form-card{transition:box-shadow .35s ease}
  .form-card:focus-within{box-shadow:0 20px 48px -26px rgba(11,15,25,.38)}
  .pick{transition:transform .18s ease,border-color .18s ease,
    background-color .18s ease,color .18s ease}
  .pick:hover{transform:translateY(-1px);border-color:var(--steel)}
}

/* about opens on the people: the claim and the panel line came out on
   Dixon's call, so the header is a name-plate and the page starts at the
   photographs */
.subhero-min{padding-bottom:clamp(18px,2.4vw,30px)}

/* ── the questionnaire in the site's own voice ────────────────────────
   The questions moved to reading size earlier; Dixon wanted them to
   carry the brand as well. They are set in Jost now — the lockup's own
   face — a step larger and in full ink, with the inputs and chips
   brought up to match so the form reads as one piece of typography
   rather than system-default fields under styled questions. */
.lbl{font:500 16.5px/1.4 "Jost",system-ui,sans-serif;
  letter-spacing:.012em;color:var(--ink);margin-bottom:8px}
.fld{font-size:15px}
select.fld{font-size:15px}
textarea.fld{font-size:15px}
.pick{font-size:14.5px;padding:11px 18px;border-radius:3px}
.form-card .rcard-head span{font-size:11.5px;letter-spacing:.14em}
.form-card .rcard-head span:first-child{color:var(--steel)}
@media (max-width:640px){
  .lbl{font-size:16.5px}
}

/* ── get something back ───────────────────────────────────────────────
   Dixon's outcome block under the hero sub: the mono label the triad
   used, one plain sentence, the five measurable things as hairline
   chips, and the before/after promise in a quiet line. */
.hero-gain{margin-top:clamp(18px,2.2vw,28px);max-width:54ch}
.gain-lede{font-size:16px;line-height:1.55;color:var(--dim);margin:10px 0 0}
.gain-list{list-style:none;display:flex;flex-wrap:wrap;gap:8px 10px;
  margin:14px 0 0;padding:0}
.gain-list li{font-family:"IBM Plex Mono",ui-monospace,monospace;
  font-size:12px;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--steel);border:1px solid rgba(107,139,164,.42);border-radius:999px;
  padding:6px 13px}
@media (max-width:640px){
  .gain-list li{padding:7px 12px}
}

  /* ---- legal pages -------------------------------------------------------
     Long-form text and nothing else. No cards, no rails, no side column: the
     measure is the only thing being designed here, and 68ch is where a
     paragraph of this type stops needing effort to read. Headings are Jost
     300 like every other heading on the site, one step down from a section
     title because a clause is not a chapter. */
  .legal{background:var(--paper);border-top:1px solid var(--line)}
  /* the meter and the body share one measure, and both start at the same
     left edge as the page title above them */
  .legal-meta{max-width:68ch;font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:10.5px;
    letter-spacing:.12em;text-transform:uppercase;color:var(--faint);
    margin:0 0 36px;padding-bottom:18px;border-bottom:1px solid var(--line)}
  .legal-body{max-width:68ch}
  .legal-body h2{font-family:"Jost",system-ui,sans-serif;font-weight:300;font-size:27px;
    letter-spacing:-.02em;line-height:1.2;margin:46px 0 13px;color:var(--ink)}
  /* one step down from a clause heading, for the sub-parts of a long one.
     Set in Switzer, not Jost: at this size the display face stops helping. */
  .legal-body h3{font-size:16px;font-weight:500;letter-spacing:-.01em;
    margin:28px 0 9px;color:var(--ink)}
  .legal-body>p:first-child{font-size:16px;color:var(--ink)}
  .legal-body p{margin:0 0 14px;color:var(--dim)}
  .legal-body .addr{margin:0 0 4px}
  .legal-body ul{margin:0 0 14px;padding-left:19px;color:var(--dim)}
  .legal-body li{margin:0 0 7px}
  .legal-body a{color:var(--steel-dim);border-bottom:1px solid rgba(93,114,142,.38)}
  .legal-body a:hover{color:var(--ink);border-color:var(--ink)}
  @media (max-width:640px){
    .legal-body h2{font-size:22px;margin-top:36px}
    .legal-body{max-width:none}
  }

  /* Responsible AI: the statement is numbered principles, so the number is set
     as a landmark rather than as part of the sentence. */
  .legal-body h2 .rai-n{font-family:"IBM Plex Mono",ui-monospace,monospace;
    font-size:12px;letter-spacing:.1em;color:var(--steel-dim);
    margin-right:12px;vertical-align:middle}
.roi-page .value-card{min-height:clamp(440px,46vw,640px)}

/* ── ROI page, second pass (6 September 2026) ─────────────────────────
   A currency switch above the card (scenario pills removed 8 Sep); a fourth slider
   for the share we can take on; the result panel shows the reclaimed hours
   as a bar against the task's total, then four figures. Same steel, ink
   and hairlines as the rest of the site; nothing new in the palette. */
.roi-top{display:flex; justify-content:space-between; align-items:flex-end; gap:20px 32px; flex-wrap:wrap; margin-bottom:18px}
.roi-cur{display:flex; align-items:center; flex-wrap:wrap; gap:8px}
.roi-top-lbl{font-size:11.5px; font-weight:500; letter-spacing:.16em; text-transform:uppercase; color:var(--dim); margin-right:6px; opacity:.75}
.roi-c{cursor:pointer}
.roi-top .pill.on,.roi-top .pill.on:hover{background:var(--ink) !important; color:#FFF !important; border-color:var(--ink) !important}
.roi-bar{height:6px; background:rgba(242,246,250,.16); border-radius:3px; margin-top:22px; overflow:hidden}
.roi-bar i{display:block; height:100%; background:#F2F6FA; border-radius:3px; transition:width .45s cubic-bezier(.16,.84,.44,1)}
.roi-bar-note{margin:10px 0 0; font-size:13px; color:#B7C6D6}
.roi-bar-note span{color:#F2F6FA; font-weight:600}
.roi-stats{display:grid; grid-template-columns:1fr 1fr; gap:18px 24px; margin-top:26px; padding-top:22px; border-top:1px solid rgba(242,246,250,.22)}
.roi-stat span{display:block; font-size:12.5px; color:#B7C6D6; margin-bottom:4px}
.roi-stat b{display:block; font-size:clamp(20px,1.9vw,26px); font-weight:600; color:#FFF; letter-spacing:-.01em; white-space:nowrap}
.roi-cta{margin-top:26px; background:#F2F6FA; color:var(--ink); border-color:#F2F6FA}
.roi-cta::before{background:#FFF}
.roi-cta:hover,.roi-cta:focus-visible{background:#F2F6FA; color:var(--ink); border-color:#FFF}
.roi-page .value-card{min-height:0}
.roi-page .value-inputs{justify-content:flex-start; padding-top:clamp(18px,2.4vw,34px); padding-bottom:clamp(18px,2.4vw,34px)}
.roi-page .vrow{padding:18px 0 22px}
@media (prefers-reduced-motion:reduce){.roi-bar i{transition:none}}
@media (max-width:640px){.roi-stats{grid-template-columns:1fr 1fr; gap:14px 16px}}

/* ── the orbit on the stance band (6 September 2026) ─────────────────
   Words left, ring right. The band's own navy stays; the ring, dots and
   card use the band's existing text tints (#F2F6FA, #8FAECB, #B7C4D4)
   and nothing from outside the palette. */
/* The band's own grid: the label across the top, the words on the left,
   the ring filling the right half. The 200px label column the other bands
   use left a third of the width empty here (Dixon, 6 September 2026). */
.stance .kyn-in{align-items:center; row-gap:28px}
.stance .kyn-label{grid-column:1 / -1}
.stance .kyn-say{max-width:none}
.stance .stance-p{max-width:54ch}
@media (min-width:1000px){
  .stance .kyn-in{grid-template-columns:minmax(0,1fr) minmax(400px,46%); column-gap:clamp(40px,6vw,110px)}
  .stance .kyn-body{grid-column:1; grid-row:2}
  .orbit{grid-column:2; grid-row:2}
}
.orbit{position:relative; width:min(100%,560px); margin:0 auto; justify-self:end; touch-action:manipulation}
.orbit-stage{position:relative; aspect-ratio:1}
@media (max-width:999px){.orbit{width:min(100%,420px); margin-top:8px}}
.orbit-ring{position:absolute; inset:12%; border-radius:50%; border:1px solid rgba(242,246,250,.16)}
.orbit-core{position:absolute; left:50%; top:50%; width:88px; height:88px; margin:-44px 0 0 -44px; border-radius:50%;
  background:#F2F6FA; box-shadow:0 0 0 1px rgba(242,246,250,.35), 0 0 0 12px rgba(143,174,203,.08), 0 0 60px rgba(143,174,203,.32);
  display:flex; align-items:center; justify-content:center; z-index:5}
/* The mark is bottom-heavy: its area centroid sits at y 68 of 100, so a
   box-centred copy reads as sitting low in the circle. The 18 units of
   padding under the artwork in its viewBox lift it by half that, and the
   larger box keeps the drawn mark at the same 44px (8 September 2026). */
.orbit-mark{display:block; width:52px; height:52px}
.orbit-core i{position:absolute; inset:-14px; border-radius:50%; border:1px solid rgba(242,246,250,.28); animation:orbit-ping 2.4s cubic-bezier(0,0,.2,1) infinite}
.orbit-core i+i{inset:-26px; animation-delay:1.2s}
@keyframes orbit-ping{0%{transform:scale(.7); opacity:.8} 100%{transform:scale(1.5); opacity:0}}
.orbit-node{position:absolute; left:50%; top:50%; width:0; height:0; border:0; background:none; padding:0; cursor:pointer;
  display:flex; flex-direction:column; align-items:center; will-change:transform; transition:opacity .4s}
.orbit-dot{flex:none; box-sizing:border-box; display:flex; align-items:center; justify-content:center; width:46px; height:46px; margin:-23px 0 0 -23px; border-radius:50%;
  background:#0C1826; border:1.5px solid rgba(242,246,250,.42); color:#F2F6FA; font-size:15px; font-weight:600; letter-spacing:.02em;
  transition:transform .3s, background .3s, color .3s, border-color .3s}
.orbit-node:hover .orbit-dot{border-color:#F2F6FA}
.orbit-node.on .orbit-dot{background:#F2F6FA; color:#0B0F14; border-color:#F2F6FA; transform:scale(1.25); box-shadow:0 0 24px rgba(242,246,250,.35)}
.orbit-lbl{position:absolute; top:28px; left:50%; transform:translateX(-50%); white-space:nowrap;
  font-size:11.5px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:#B7C4D4}
.orbit-node.on .orbit-lbl{color:#F2F6FA}
.orbit-node:focus-visible .orbit-dot{outline:2px solid #F2F6FA; outline-offset:4px}
@media (prefers-reduced-motion:reduce){.orbit-core i{animation:none; opacity:.35} .orbit-node{transition:none}}
.orbit-dot svg{width:20px; height:20px; display:block}

/* ── ROI hero: words left, the value adding up on the right ──────────── */
.roi-hero{display:grid; grid-template-columns:minmax(0,1fr); gap:32px; align-items:center}
@media (min-width:1000px){.roi-hero{grid-template-columns:minmax(0,1fr) minmax(380px,520px); gap:clamp(32px,5vw,80px)}}
.roi-hero-chart{position:relative; display:flex; flex-direction:column; background:linear-gradient(160deg,#16283C,#0B0F14 80%); border:1px solid rgba(242,246,250,.18); border-radius:2px; overflow:hidden; box-shadow:0 24px 60px rgba(11,15,25,.18)}
.roi-hero-plot{position:relative; aspect-ratio:600/300; min-height:230px}
.roi-svg{position:absolute; inset:0; width:100%; height:100%}
.roi-hero-num{position:absolute; left:clamp(16px,3vw,28px); top:clamp(16px,3vw,26px); pointer-events:none}
.roi-hero-num b{display:block; font-size:clamp(34px,4vw,54px); font-weight:600; letter-spacing:-.03em; line-height:1; color:#FFF; white-space:nowrap}
.roi-hero-num span{display:block; margin-top:8px; font-size:13px; color:#8FAECB; max-width:22ch}
.roi-hero-side{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; padding:14px clamp(16px,3vw,28px) 16px; border-top:1px solid rgba(242,246,250,.16); background:rgba(11,15,20,.35)}
.roi-hero-side b{display:block; font-size:17px; font-weight:600; color:#FFF; letter-spacing:-.01em; line-height:1.1; white-space:nowrap}
.roi-hero-side span{display:block; font-size:11.5px; color:#8FAECB; margin-top:2px; white-space:nowrap}
.roi-hero-axis{position:absolute; left:12px; right:12px; bottom:8px; display:flex; justify-content:space-between; font-size:10.5px; letter-spacing:.12em; text-transform:uppercase; color:#8FAECB; opacity:.8}
@media (max-width:640px){.roi-hero-side{grid-template-columns:1fr 1fr} .roi-hero-num b{font-size:30px}}
.orbit-caption{margin:34px 0 0; text-align:center; font-size:15px; line-height:1.5; color:#8FAECB; min-height:3em; padding:0 12px; transition:color .3s}
.orbit-caption.on{color:#F2F6FA}

/* ── the closer's clock (6 September 2026) ──────────────────────────── */
.closer-in{display:grid; grid-template-columns:minmax(0,1fr); gap:36px; align-items:center}
@media (min-width:1000px){.closer-in{grid-template-columns:minmax(0,1fr) minmax(360px,440px); gap:clamp(40px,6vw,100px)}}
.closer-clock{position:relative; padding:clamp(22px,3vw,34px); background:rgba(11,15,20,.42); border:1px solid rgba(242,246,250,.22); border-radius:2px; backdrop-filter:blur(8px); color:#F2F6FA}
.closer-clock-h{margin:0; font-size:12px; font-weight:600; letter-spacing:.14em; text-transform:uppercase; color:#B7C4D4}
.clock-row{display:flex; align-items:flex-start; gap:10px; margin-top:18px}
.clock-cell{display:flex; flex-direction:column; align-items:center; min-width:3.2ch}
.clock-cell b{font-family:"IBM Plex Mono",ui-monospace,monospace; font-size:clamp(34px,3.6vw,50px); font-weight:500; line-height:1; letter-spacing:-.02em; color:#FFF; font-variant-numeric:tabular-nums}
.clock-cell span{margin-top:8px; font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:#8FAECB}
.clock-reset{display:inline-flex; align-items:center; gap:8px; margin-top:18px; padding:10px 14px; border:1px solid rgba(242,246,250,.35); border-radius:2px; color:#F2F6FA; font-size:12px; font-weight:600; letter-spacing:.1em; text-transform:uppercase; text-decoration:none}
.clock-reset svg{width:14px; height:14px}
.clock-reset:hover{background:#F2F6FA; color:var(--ink); border-color:#F2F6FA}
.roi-grid line{stroke:rgba(242,246,250,.08); stroke-width:1}
.roi-tip{position:absolute; transform:translate(-50%,-125%); padding:8px 10px; background:#F2F6FA; color:var(--ink); border-radius:2px; font-size:12px; line-height:1.3; white-space:nowrap; pointer-events:none; box-shadow:0 8px 24px rgba(11,15,25,.35); z-index:3}
.roi-tip b{display:block; font-size:15px; font-weight:600}
.roi-tip span{display:block; color:var(--dim); opacity:.85}
.roi-legend{position:absolute; right:clamp(12px,2vw,20px); top:clamp(14px,2.4vw,22px); display:grid; gap:6px; font-size:11px; letter-spacing:.06em; color:#B7C4D4; text-align:right}
.roi-legend span::before{content:""; display:inline-block; width:18px; height:0; margin-right:8px; vertical-align:middle; border-top:2px solid #3B8CFF}
.roi-legend .lg-cost::before{border-top:2px dashed #8FAECB}
@media (max-width:640px){.roi-legend{display:none}}

