/* ============================================================
   SERVICE PAGES — /services and the four disciplines
   Layered on css/home.css, which brings the tokens, the glass, the nav spine,
   the beacon, the sun and moon, the frames, the contact block and the footer.
   Only what a service page needs beyond that lives here.

   Direction. The homepage is the reference, and its lineage is the
   architectural-editorial one: monumental display type with tight negative
   tracking, hairline rules as the only dividers, generous vertical rhythm,
   flat surfaces layered by colour rather than shadow, and media kept in its
   own framed container.

   The one thing deliberately rejected: floating pricing cards with elevation
   and a highlighted "most popular" ribbon. That is the SaaS convention and it
   would collapse this page into every other agency site. Pricing here is set
   as ruled editorial rows — the same idiom as the work index and the case
   study "what we did" list — so a plan reads as a line in a document rather
   than a product in a shop.
   ============================================================ */

/* ---------- hero ----------
   No image. The type is the hero, as it is on the homepage. */
.shero{
  /* --sheet-top is where the picture begins, measured from the top of the
     hero; it sits just below the fold so the page has to be scrolled before
     the picture is seen. --sheet-h is that picture's height, which follows
     from its width and 16/10, and the hero is made tall enough to hold both. */
  --sheet-top:54svh;
  --sheet-h:min(32.5vw,475px);
  padding-top:calc(var(--nav-h) + clamp(30px,6vh,80px));
  padding-bottom:clamp(20px,4vw,60px);
  min-height:calc(var(--sheet-top) + var(--sheet-h) + clamp(20px,4vw,60px));
  display:flex;flex-direction:column;justify-content:flex-start}
.shero__eyebrow{display:inline-flex;align-items:center;gap:12px;margin-bottom:22px}
.shero__title{
  font-size:clamp(46px,7.4vw,112px);font-weight:700;
  letter-spacing:-.045em;line-height:.95;text-wrap:balance;max-width:13ch;
}
.shero__lede{
  margin-top:clamp(22px,2.6vw,34px);max-width:38ch;
  font-size:var(--fs-lede);font-weight:300;line-height:1.5;letter-spacing:-.015em;opacity:.78;
}
.shero__act{display:flex;flex-wrap:wrap;gap:14px 26px;align-items:center;margin-top:clamp(26px,3vw,40px)}

/* ---------- the hero picture ----------
   The sheet sits in the right half of the hero and behind the text, so a long
   headline runs over it rather than being pushed down the page. js/sheet.js
   draws it; this places it and hangs its shadow.

   No frame and no background: the canvas is transparent and the shadow comes
   from its own alpha, so it follows the cloth as it moves rather than sitting
   in a fixed box behind it. */
.shero{position:relative;isolation:isolate}
.shero > *{position:relative;z-index:1}
.shero__sheet{
  position:absolute;z-index:0;
  top:var(--sheet-top);right:calc(var(--gap) * -1);
  width:min(52vw,760px);aspect-ratio:16/10;
  pointer-events:none;              /* the scroll drives it; it takes no clicks */
}
/* The canvas is larger than the box on every side, by BLEED_X and BLEED_Y in
   js/sheet.js, so the corners have room to bend when the page moves fast; the
   picture itself is drawn at the box's size and place. What pokes past the
   viewport's edge is clamped away by the page's overflow-x:hidden. */
.shero__sheet canvas{
  display:block;position:absolute;left:-5%;top:-12%;width:110%;height:124%;
  filter:drop-shadow(0 30px 40px rgba(var(--shadow-rgb),.30))
         drop-shadow(0 5px 9px rgba(var(--shadow-rgb),.16));
}
.sheet3d__still{display:block;width:100%;height:auto}
/* /services: the client whose picture is showing, under it, linked to the
   case study (js/sheet.js swaps it with the picture). The canvas draws the
   picture at 89% of its box, centred, so the caption starts at the
   picture's left edge, 5.5% in, and hangs just under its bottom edge. */
.shero__cap{
  position:absolute;left:5.5%;top:calc(94.5% + 12px);margin:0;
  pointer-events:auto;white-space:nowrap;
  transition:opacity .26s ease;
}
.shero__cap.is-swap{opacity:0}
.shero__cap a{color:inherit;text-decoration:none;opacity:.62;transition:opacity .2s ease}
.shero__cap a:hover,.shero__cap a:focus-visible{opacity:1}
.shero__cap span::before{content:"\00b7";margin:0 .6em}
.shero__sheet:has(.sheet3d__still) .shero__cap{left:0;top:calc(100% + 12px)}
/* the text has to stay readable where it crosses the picture */
.shero__title,.shero__lede{text-shadow:0 1px 26px rgba(var(--paper-rgb),.72)}

@media (max-width:1100px){
  .shero{--sheet-h:min(38.75vw,387.5px)}
  .shero__sheet{width:min(62vw,620px)}
}
/* Narrow: side by side stops working, so the sheet goes back under the text
   where it has room to be seen. */
@media (max-width:820px){
  /* Side by side stops working, so the picture goes full width under the
     text, straight after it. (It used to wait at the fold, which on a phone
     left a screen's worth of empty page between the words and the picture;
     Aston, 2026-09-29: "too spread out, far away from the top header".) */
  .shero{min-height:0}
  .shero__sheet{
    position:relative;top:auto;right:auto;
    width:100%;margin-top:clamp(36px,9vw,56px);
  }
  /* js/sheet.js draws the picture about 89% of its box, leaving the corners
     room to bend; on a phone that left a gap either side of it (Aston,
     2026-09-29), so the box is that much wider and centred, and the picture
     itself lines up with the words above it */
  .shero__sheet{width:calc(100% / .89);margin-left:calc((100% / .89 - 100%) / -2)}
  .shero__sheet .sheet3d__still{width:89%;margin-inline:auto}
  /* room under the picture for the caption, so it never meets the band below */
  .shero__sheet:has(.shero__cap){margin-bottom:22px}
  .shero__sheet:has(.sheet3d__still) .shero__cap{left:5.5%;top:calc(89% + 12px)}
  /* the sheet's canvas reaches past the screen's edge to give its corners
     room; clip it there, so the page never scrolls sideways */
  .shero{overflow-x:clip}
  .shero__title,.shero__lede{text-shadow:none}
}

/* ---------- organic social (/social-media-management) ----------
   The type is the hero and a pair of eyes takes the sheet's place, up beside
   the headline rather than below the fold: they are the joke, so they are in
   view at once. The still is the pair rendered in Blender (docs/eyes/eyes.py);
   on a capable device js/eyes3d.js swaps in the same pair live, following the
   pointer, and the still stays underneath as the fallback. The canvas reaches
   past the still by BLEED in js/eyes3d-scene.js. */
.shero--eyes{
  --eyes-w:min(46vw,700px);
  --sheet-h:0px;
  min-height:calc(var(--nav-h) + clamp(30px,6vh,80px) + var(--eyes-w) * .493 + clamp(20px,4vw,60px));
}
/* the still is the render cut to the pair (1128 x 556 of it): the box keeps
   that shape so the live camera's view lands on it exactly */
.shero__eyes{
  position:absolute;z-index:0;margin:0;
  top:calc(var(--nav-h) + clamp(30px,6vh,80px) + 4vh);right:calc(var(--gap) * -1);
  width:var(--eyes-w);aspect-ratio:1128/556;
  pointer-events:none;
}
.shero__eyes img{display:block;width:100%;height:auto;transition:opacity .8s ease}
.shero__eyes canvas{
  position:absolute;z-index:2;
  left:-5%;top:-5%;width:110%;height:110%;
  opacity:0;transition:opacity .8s ease;
}
.shero__eyes.is-3d canvas{opacity:1;pointer-events:auto}
.shero__eyes.is-3d img{opacity:0}
@media (max-width:1100px){
  .shero--eyes{--eyes-w:min(48vw,560px)}
}
@media (max-width:820px){
  .shero--eyes{min-height:0}
  .shero__eyes{position:relative;top:auto;right:auto;width:min(100%,520px);margin-top:clamp(24px,5vw,40px)}
}
.shero__act .link{font-size:var(--fs-body)}
/* ---------- the reach ----------
   The page's big number, set as large as it can be and still fit its seven
   figures on one line, with what it means under it, three lines that put it
   in perspective on a rule of their own, and the fine print. It follows the
   platforms band, whose rule is its top, and the ruled lines below are its
   own: no rule sits on another. */
.reach{margin-top:var(--s-sec);--board-ar:7.14/1.72}   /* the board's box: six digits and a comma, plus its margin (board3d-scene.js) */
.reach > .label{display:inline-flex;align-items:center;gap:12px}
/* One sentence in three lines, set like the closing question: the words at
   display size, the figure between them as a split-flap board the full
   measure wide. The board is seven tiles with the digits set in type; the
   live one draws over them in the same box, so nothing moves when it takes
   over, and where it cannot run the tiles are the board. */
.reach__line{
  margin-top:clamp(18px,2.2vw,30px);
  font-size:min(var(--fs-h2),7.4vw);font-weight:300;
  letter-spacing:-.04em;line-height:1.02;text-wrap:balance;
}
.reach__stage{
  position:relative;width:100%;aspect-ratio:var(--board-ar);
  margin-top:clamp(14px,1.8vw,26px);
  container-type:inline-size;
}
.reach > .label + .reach__stage{margin-top:clamp(18px,2.2vw,30px)}   /* straight from the label to the board (Aston, 2026-09-30) */
.reach__ast{margin-left:.04em;color:inherit;text-decoration:none;font-size:.6em;vertical-align:.7em;line-height:0}
.reach__ast:hover,.reach__ast:focus-visible{text-decoration:underline;text-underline-offset:3px}
/* a footnote further down the page: small, quiet, a measure of its own */
.fnote{margin-top:clamp(28px,3.4vw,48px);max-width:80ch;font-size:13px;line-height:1.6;opacity:.62;scroll-margin-top:40vh}
.reach__board{position:absolute;inset:0;margin:0;opacity:0;transition:opacity .8s ease;cursor:pointer}
.reach__board canvas{position:absolute;inset:0;width:100%;height:100%}
.reach__board.is-3d{opacity:1}
/* The board without WebGL, to the live board's measures (board3d-scene.js;
   one unit of it is 14cqw of the stage): one black face, a window per digit,
   the comma painted on the face. In each window the upper half is a shade
   lighter than the lower, and the slot between them carries the axle's gold
   line. The same in either theme: a board is black. */
.reach__n{
  position:absolute;left:50%;top:50%;margin:0;transform:translate(-50%,-50%);
  display:grid;grid-template-columns:repeat(3,13.08cqw) 4.96cqw repeat(3,13.08cqw);grid-template-rows:18.96cqw;
  column-gap:1.9cqw;padding:1.58cqw;background:#0B0B0D;
  transition:opacity .8s ease;
}
.reach__n span{
  position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;
  background:linear-gradient(#18181c 0 50%,#101013 50%);color:#F6F5F8;border-radius:.7cqw;
  font-size:16.9cqw;font-weight:500;letter-spacing:-.02em;line-height:1;font-variant-numeric:tabular-nums;
}
.reach__n span::after{
  /* the slot between the halves, with the axle's gold line in the middle of
     it, clear of both, in the live board's proportions */
  content:"";position:absolute;left:0;right:0;top:50%;height:1.4cqw;margin-top:-.7cqw;
  background:linear-gradient(#0A0A0C 0 38%,#C9A24E 38% 62%,#0A0A0C 62%);
}
.reach__n .reach__sep{background:none;border-radius:0;overflow:visible}
.reach__n .reach__sep::after{content:none}
.reach__stage:has(.reach__board.is-3d) .reach__n{opacity:0}
/* in perspective: one quiet line, not a table */
.reach__scale{margin-top:clamp(22px,2.6vw,36px);max-width:46ch;font-size:var(--fs-lede);font-weight:300;line-height:1.45;letter-spacing:-.015em;opacity:.72}
/* under it, the way in */
.reach__cta{display:inline-block;margin-top:clamp(22px,2.6vw,36px);font-size:var(--fs-body)}

/* questions as ruled rows, the question set narrower than a step's title */
.steps--faq h3{max-width:24ch}
.reels > figure{margin:0}

/* ---------- the running band of platforms ----------
   A quiet marquee, not a logo wall: these are names the client typed, not
   brand assets we hold. It pauses on hover and stands still for anyone who
   has asked for less motion. */
.rail{margin-bottom:var(--s-block);border-top:var(--hair);border-bottom:var(--hair);overflow:hidden;padding:18px 0}
.rail__track{display:flex;gap:clamp(28px,4vw,64px);width:max-content;animation:rail 34s linear infinite}
.rail:hover .rail__track,.rail:focus-within .rail__track{animation-play-state:paused}
.rail__item{
  font-size:13px;letter-spacing:.09em;text-transform:uppercase;opacity:.62;white-space:nowrap;
  display:inline-flex;align-items:center;gap:12px;
}
.rail__item::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.6}
@keyframes rail{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.rail__track{animation:none}}

/* ---------- approach / what we do ----------
   Numbered ruled rows. Same idiom as a case study's "what we did". */
.steps{border-top:var(--hair)}
.step{
  display:grid;grid-template-columns:2.6em minmax(0,1.1fr) minmax(0,1.5fr);
  gap:clamp(16px,3vw,48px);align-items:start;
  padding:26px 0;border-bottom:var(--hair);
}
.step h3{font-size:var(--fs-h3);font-weight:400;letter-spacing:-.02em;text-wrap:balance}
.step p{font-size:var(--fs-body);opacity:.7;line-height:1.6;max-width:52ch}

/* ---------- plans ----------
   A plan is a ruled row, not a card: label and name on the left, the figure
   set large on the right, the detail beneath. The one "featured" plan is
   marked by a hairline inset and a mark, never by a coloured ribbon. */
/* Same again for every ruled block on these pages: one line per break. */
.sec__head:has(+ .plans),
.sec__head:has(+ .hub),
.sec__head:has(+ .panels),
.sec__head:has(+ .steps),
.sec__head:has(+ .rail){border-bottom:0}

.plans{border-top:var(--hair)}
.plan{padding:clamp(28px,3.4vw,44px) 0;border-bottom:var(--hair)}
.plan__head{
  display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:18px clamp(20px,4vw,60px);align-items:baseline;
}
.plan__name{font-size:var(--fs-h3);font-weight:400;letter-spacing:-.025em;margin-top:12px;text-wrap:balance}
.plan__fig{text-align:right;white-space:nowrap}
.plan__n{
  display:block;font-size:clamp(34px,4.6vw,64px);font-weight:300;
  letter-spacing:-.045em;line-height:1;font-variant-numeric:tabular-nums;
}
.plan__per{display:block;margin-top:10px;font-size:12px;letter-spacing:.06em;text-transform:uppercase;opacity:.62}
.plan__list{margin-top:clamp(20px,2.4vw,30px);display:grid;gap:12px;max-width:62ch}
.plan__list li{
  display:grid;grid-template-columns:18px minmax(0,1fr);gap:14px;align-items:start;
  font-size:var(--fs-body);line-height:1.5;opacity:.82;
}
.plan__list li::before{content:"";margin-top:.62em;height:1px;background:currentColor;opacity:.45}
.plan__list a{text-decoration:underline;text-underline-offset:.16em;text-decoration-thickness:1px}
.plan__note{
  margin-top:20px;padding-top:16px;border-top:var(--hair);
  font-size:13px;line-height:1.6;opacity:.62;max-width:62ch;
}
/* The emphasised plan used to be inset behind a hairline. That gave it a left
   border and a left padding no other tier had, so the two tiers no longer
   started at the same edge or lined up with the header above them, and it read
   as something half-finished rather than as emphasis. Every tier is the same
   row now and the weight of the name is what carries the emphasis. */
.plan--lead .plan__name{font-weight:700}

/* ---------- plan chips (hosting & maintenance) ----------
   Each care plan has its own chip, rendered in Blender: the brand's quarter,
   half and whole disc on the face show how much of the site the plan covers.
   Black ceramic in the light theme and white ceramic in the dark one, swapped
   by home.js (data-light / data-dark). They picture the plan named beside
   them, so they carry no alt of their own.

   The renders are the chip alone; its shadow is drawn here, like the
   monument's. One shadow serves all six: the same body, camera and crop
   (docs/maintenance-3d/chips.shadow.json). */
.plan--chip{display:grid;grid-template-columns:clamp(150px,17vw,230px) minmax(0,1fr);gap:clamp(20px,3vw,48px);align-items:start}
.plan__chip{
  position:relative;margin-top:2px;
  --chip-shadow:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-71 -13 963 601' preserveAspectRatio='none'%3E%3Cdefs%3E%3Cfilter id='s0' filterUnits='userSpaceOnUse' x='-71' y='-13' width='963' height='601'%3E%3CfeGaussianBlur stdDeviation='16'/%3E%3C/filter%3E%3Cfilter id='s1' filterUnits='userSpaceOnUse' x='-71' y='-13' width='963' height='601'%3E%3CfeGaussianBlur stdDeviation='10'/%3E%3C/filter%3E%3Cfilter id='s2' filterUnits='userSpaceOnUse' x='-71' y='-13' width='963' height='601'%3E%3CfeGaussianBlur stdDeviation='5'/%3E%3C/filter%3E%3Cfilter id='s3' filterUnits='userSpaceOnUse' x='-71' y='-13' width='963' height='601'%3E%3CfeGaussianBlur stdDeviation='2'/%3E%3C/filter%3E%3Cfilter id='s4' filterUnits='userSpaceOnUse' x='-71' y='-13' width='963' height='601'%3E%3CfeGaussianBlur stdDeviation='2'/%3E%3C/filter%3E%3C/defs%3E%3Cpolygon filter='url(%23s0)' fill-opacity='0.25' points='21.5,290.2 708.2,520.4 844.3,179.8 274.6,35.2'/%3E%3Cpolygon filter='url(%23s1)' fill-opacity='0.45' points='-41.1,318.9 644.3,557.6 812.1,167.9 255.9,27.2'/%3E%3Cpolygon filter='url(%23s2)' fill-opacity='0.8' points='11.7,281.5 653.7,495.9 784.9,199.0 238.5,56.3'/%3E%3Cpolygon filter='url(%23s3)' fill-opacity='0.6' points='16.2,330.2 579.6,525.1 592.9,497.4 36.7,309.0'/%3E%3Cpolygon filter='url(%23s4)' fill-opacity='0.6' points='281.9,55.6 747.7,175.8 755.7,159.2 294.9,42.1'/%3E%3C/svg%3E");
}
.plan__chip img{position:relative;z-index:1;display:block;width:100%;height:auto}
.plan__chip::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  left:-8.88%;right:-11.50%;top:-2.44%;bottom:-10.53%;
  background:rgba(var(--shadow-rgb),.5);
  -webkit-mask:var(--chip-shadow) 0 0/100% 100% no-repeat;
          mask:var(--chip-shadow) 0 0/100% 100% no-repeat;
}
.plan__body{min-width:0}
@media (max-width:640px){
  .plan--chip{grid-template-columns:1fr;gap:10px}
  .plan__chip{width:min(60vw,230px)}
}

.maint__marks{margin-top:clamp(24px,3vw,36px)}
/* Kinsta's badge carries its own plate: match it to the height of the plated
   WordPress mark beside it rather than wrapping it in a second plate */
.maint__kinsta{display:block;height:58px;width:auto}
@media (max-width:640px){ .maint__kinsta{height:54px} }

/* ---------- the WordPress monument (hosting & maintenance) ----------
   The WordPress mark carved into a gold medallion on a stepped plinth,
   rendered in Blender, standing beside the closing question. The render is
   the object alone on transparency; its shadow is the ::before below. */
.contact--monument .contact__grid{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);align-items:center}
.contact__monument{position:relative;margin:0;justify-self:end;width:min(100%,520px);perspective:1100px}
.contact__monument img{
  position:relative;z-index:1;
  display:block;width:100%;height:auto;
  transform:rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg));transform-origin:50% 62%;
}
/* The shadow belongs to the page, not to the picture. Baked into the render
   it could only ever be a grey patch inside the image's rectangle; drawn here
   it has no edge to be cut off at. It is the plinth's own footprint,
   projected through the render's camera and blurred three ways
   (docs/maintenance-3d/monument.shadow.json): tight where the base touches
   the ground, softer around it, and a faint lean to the right, away from the
   key light. The mask holds only the shape; the colour is the page's shadow
   ink. While the monument tilts, the
   shadow turns and slides with the foot of the plinth rather than with the
   picture, so the base stays planted. */
.contact__monument{
  --monument-shadow:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-66 610 1071 342' preserveAspectRatio='none'%3E%3Cdefs%3E%3Cfilter id='s0' filterUnits='userSpaceOnUse' x='-66' y='610' width='1071' height='342'%3E%3CfeGaussianBlur stdDeviation='20'/%3E%3C/filter%3E%3Cfilter id='s1' filterUnits='userSpaceOnUse' x='-66' y='610' width='1071' height='342'%3E%3CfeGaussianBlur stdDeviation='12'/%3E%3C/filter%3E%3Cfilter id='s2' filterUnits='userSpaceOnUse' x='-66' y='610' width='1071' height='342'%3E%3CfeGaussianBlur stdDeviation='3'/%3E%3C/filter%3E%3C/defs%3E%3Cpolygon filter='url(%23s0)' fill-opacity='0.3' points='-5.9,768.9 869.8,892.5 944.9,750.2 213.6,669.9'/%3E%3Cpolygon filter='url(%23s1)' fill-opacity='0.5' points='-22.5,767.7 832.5,888.6 913.2,750.8 194.5,671.3'/%3E%3Cpolygon filter='url(%23s2)' fill-opacity='0.95' points='11.8,763.9 809.6,874.9 885.7,754.5 201.5,677.8'/%3E%3C/svg%3E");
}
.contact__monument::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  left:-7.33%;right:-11.67%;top:69.00%;bottom:-7.69%;;
  background:rgba(var(--shadow-rgb),.56);
  -webkit-mask:var(--monument-shadow) 0 0/100% 100% no-repeat;
          mask:var(--monument-shadow) 0 0/100% 100% no-repeat;
  transform-origin:48.4% 43.6%;
  transform:translate(calc(var(--tilt-y,0) * -1px),calc(var(--tilt-x,0) * .5px)) rotate(calc(var(--tilt-y,0) * -.16deg));
}
/* The live monument (js/monument3d.js) draws over the still once its first
   frame is ready, and the still and its shadow fade out beneath it. Its canvas
   reaches past the still on every side, by BLEED in js/monument3d-scene.js, to
   hold the shadow and the turn. */
.contact__monument canvas{
  position:absolute;z-index:2;pointer-events:none;
  left:-8%;top:-2%;width:119%;height:112%;
  opacity:0;transition:opacity .8s ease;
}
.contact__monument img,.contact__monument::before{transition:opacity .8s ease}
/* Among the floating gold pieces (services, digital marketing: the figure is
   .contact__monument--float, data-monument3d="float"), the same monument
   floats as they do: the still bobs on their clock (translate, so the tilt's
   transform is untouched), and its shadow is their pool under it rather than
   its footprint on a floor. The live one keeps to the same float and keeps
   the pool; on /maintenance it stands as before. */
.contact__monument--float img{animation:monument-float 5.2s ease-in-out infinite}
@keyframes monument-float{0%,100%{translate:0 3%}50%{translate:0 -3%}}
.contact__monument--float::before{
  -webkit-mask:none;mask:none;
  left:10%;right:10%;top:calc(100% - 5%);bottom:auto;height:11%;border-radius:50%;
  background:radial-gradient(ellipse at 50% 50%,rgba(var(--shadow-rgb),.42),rgba(var(--shadow-rgb),.16) 45%,rgba(var(--shadow-rgb),0) 72%);
  transform-origin:50% 50%;
  animation:gold-shadow 5.2s ease-in-out infinite;
}
.contact__monument--float.is-3d::before{opacity:1}
@media (prefers-reduced-motion:reduce){.contact__monument--float img,.contact__monument--float::before{animation:none}}
.contact__monument.is-3d canvas{opacity:1}
.contact__monument.is-3d img,.contact__monument.is-3d::before{opacity:0}
@media (max-width:820px){
  .contact--monument .contact__grid{grid-template-columns:1fr}
  .contact__monument{justify-self:start;width:min(82vw,380px)}
}

/* the gold pieces beside the closing question: in home.css, since the paid
   media case studies close on the coin too */

/* ---------- the platforms a tier runs on ----------
   These are other companies' marks, so they keep their own colours: nothing
   is inverted, recoloured or forced to monochrome. That leaves a problem on
   the dark theme, where Meta's wordmark and TikTok's are near-black and simply
   disappear, and the Google Partner badge is a JPEG with a white background
   baked into it. A light plate under every mark is the one treatment that is
   correct for all of them and touches none of their colour, and it makes the
   badge's own white background read as deliberate rather than as a seam. */
.plan__badges{display:flex;flex-wrap:wrap;align-items:center;gap:10px;margin-top:clamp(20px,2.4vw,28px)}
.plan__badge{
  --bh:17px;
  display:inline-flex;align-items:center;justify-content:center;
  background:#fff;border:var(--hair);border-radius:10px;padding:10px 14px;
}
.plan__badge--lg{--bh:42px;padding:7px 11px}
/* TikTok's note is taller than its wordmark, so at the same height the word
   reads a size smaller than Meta's and LinkedIn's; this evens them by eye */
.plan__badge--tall{--bh:21px}
.plan__badge img,.plan__badge svg{display:block;width:auto;height:var(--bh)}
@media (max-width:640px){ .plan__badge{--bh:15px;padding:8px 11px} .plan__badge--lg{--bh:36px} }


/* ---------- the service panels ----------
   A checkerboard of light and dark cells on the hub, one per discipline.

   A flipped panel is not a new palette: --paper and --ink already swap between
   the two themes, so the cell just sets them to the other theme's values and
   everything inside — rules, labels, the glyph — follows on its own. That is
   why there is no second set of colours anywhere in here.

   The picture inside each cell is a still, not a hanging sheet. Five sheets on
   one page would be five WebGL contexts and five simulations for a grid the
   reader scrolls past; the cloth earns its cost in the hero, not here. */
.panels{
  margin-top:var(--s-block);
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  border-top:var(--hair);border-left:var(--hair);
}
.panel{
  position:relative;display:flex;flex-direction:column;
  padding:clamp(28px,3.4vw,54px) clamp(24px,3vw,48px) 0;
  border-right:var(--hair);border-bottom:var(--hair);
  background:var(--paper);color:var(--ink);
  text-decoration:none;overflow:hidden;
  min-height:clamp(380px,42vw,560px);
}
/* the inverted cell: the other theme's ground, whichever theme we are in */
.panel--flip{
  --paper:#0A0A0C; --ink:#F6F5F8;
  --paper-rgb:10,10,12; --ink-rgb:246,245,248;
  --line:rgba(246,245,248,.18);
  --hair:1px solid var(--line);
}
:root[data-theme="dark"] .panel--flip{
  --paper:#F6F5F8; --ink:#0A0A0C;
  --paper-rgb:246,245,248; --ink-rgb:10,10,12;
  --line:rgba(10,10,12,.18);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .panel--flip{
    --paper:#F6F5F8; --ink:#0A0A0C;
    --paper-rgb:246,245,248; --ink-rgb:10,10,12;
    --line:rgba(10,10,12,.18);
  }
}
.panel__eyebrow{display:inline-flex;align-items:center;gap:12px;margin-bottom:20px}
.panel__title{
  font-size:clamp(28px,3.1vw,46px);font-weight:700;
  letter-spacing:-.04em;line-height:1.02;text-wrap:balance;max-width:14ch;
}
.panel__say{
  margin-top:16px;max-width:38ch;font-size:15px;font-weight:300;
  line-height:1.5;letter-spacing:-.01em;opacity:.72;
}
.panel__shot{
  margin:clamp(26px,3vw,44px) calc(clamp(24px,3vw,48px) * -1) 0;
  margin-bottom:calc(clamp(28px,3.4vw,54px) * -1);
  flex:1;min-height:0;
}
.panel__shot img{
  display:block;width:100%;height:100%;object-fit:cover;object-position:50% 20%;
  transition:transform 1.1s var(--ease);
}
.panel:hover .panel__shot img{transform:scale(1.035)}
.panel__title{transition:opacity var(--t-fast) var(--ease)}
.panel:hover .panel__title{opacity:.66}
.panel:focus-visible{outline:2px solid var(--ink);outline-offset:-3px}
@media (max-width:820px){
  .panels{grid-template-columns:1fr}
  .panel{min-height:0}
}

/* ---------- the services index ----------
   One ruled row per discipline: number, name, one line, and where it starts.
   The hub is a table of contents, not a gallery of cards. */
/* ---------- the section marks keep turning ----------
   On the homepage every one of these motifs is a looping GIF, so the little
   glyph beside each label is always in motion. These pages draw the same
   motifs as SVG — the GIFs are never shipped — which left them frozen after
   the one flip they do on reveal. They now take a quarter turn on the same
   cadence as the GIFs and the glyph wall, easing into each step and holding
   it, the way the wall does. Discs are left alone: a turning circle looks
   exactly like a still one, and they have their own scale on reveal. */
@keyframes mark-turn{
  0%,14%{transform:rotate(0deg)}
  25%,39%{transform:rotate(90deg)}
  50%,64%{transform:rotate(180deg)}
  75%,89%{transform:rotate(270deg)}
  100%{transform:rotate(360deg)}
}
.mark--half,.mark--quarter,.mark--tile{
  animation:mark-turn calc(var(--loop-b) * 4) var(--ease) infinite;
}
@media (prefers-reduced-motion:reduce){
  .mark--half,.mark--quarter,.mark--tile{animation:none}
}

/* ---------- the glyph band ----------
   The homepage's wall, standing in the dead space between the section header
   and the price list. It is full bleed, so it lives outside the .wrap; the
   header already carries a rule and a block of air under it, so the band takes
   no top margin of its own and its own hairlines close the gap. */
.glyphs--band{margin-top:0}
.svc-prices{margin-top:clamp(38px,4.6vw,64px)}
/* after the five gold pieces the usual gap between sections read as a hole:
   a quarter less there (Aston, 2026-09-30) */
.five + .sec{padding-top:calc(var(--s-sec) * .75)}
.five + .sec .svc-prices{margin-top:calc(clamp(38px,4.6vw,64px) * .75)}

/* the services' head: the close moved up (2026-09-30), its consult button on a line of its own */
.parts__head .consult__btn{grid-column:1 / -1;justify-self:start;margin-top:clamp(6px,.8vw,12px)}
/* on a desktop the button stands at the heading's right, level with its last
   line, instead of hanging between the heading and the list (Aston,
   2026-09-30: "the placement of the call-to-action button is throwing me off") */
@media (min-width:900px){
  .parts__head{grid-template-columns:minmax(0,1fr) auto;column-gap:clamp(24px,4vw,64px)}
  .parts__head .h2{grid-column:1;grid-row:2}
  .parts__head .consult__btn{grid-column:2;grid-row:2;align-self:end;justify-self:end;margin:0 0 .5em}
}
.hub{border-top:var(--hair)}
/* Managed Digital Marketing, first and apart (2026-09-30): a dark band the
   row's own width plus a little bleed either side, so its words stay on the
   same lines as the rows under it; the rule above it and below it give way */
.hub:has(> .hub__row--whole:first-child){border-top:0}
.hub > .hub__row--whole{position:relative;isolation:isolate;color:var(--paper);border-bottom-color:transparent;margin-bottom:clamp(8px,1vw,14px)}   /* > for weight: the plain .hub__row rule comes later */
.hub > .hub__row--whole::before{content:"";position:absolute;inset:0 calc(clamp(16px,2vw,28px) * -1);z-index:-1;background:var(--ink);border-radius:clamp(14px,1.6vw,22px)}
.hub > .hub__row--whole .hub__say{opacity:.78}
.hub > .hub__row--whole .hub__go{opacity:.75}
.hub > .hub__row--whole:focus-visible{outline-color:var(--ink);outline-offset:clamp(20px,2.4vw,32px)}
.hub__from > span + span{margin-top:3px}   /* a second line under the unit: what the price leaves out, or the minimum */
.hub__row{
  display:grid;grid-template-columns:2.6em minmax(0,1.05fr) minmax(0,1.5fr) auto 1.4em;
  gap:clamp(14px,2.6vw,44px);align-items:baseline;
  padding:clamp(24px,2.8vw,36px) 0;border-bottom:var(--hair);
  color:inherit;text-decoration:none;
}
.hub__name{font-size:clamp(24px,2.6vw,38px);font-weight:400;letter-spacing:-.03em;text-wrap:balance}
.hub__say{font-size:var(--fs-body);opacity:.7;line-height:1.55;max-width:48ch}
.hub__from{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.hub__from b{display:block;font-size:clamp(17px,1.5vw,22px);font-weight:400;letter-spacing:-.02em}
.hub__from > span{display:block;margin-top:6px;font-size:11px;letter-spacing:.07em;text-transform:uppercase;opacity:.62}   /* the unit line only, not the currencies inside the figure */
.hub__go{opacity:.4;transition:transform var(--t-fast) var(--ease),opacity var(--t-fast) var(--ease)}
.hub__go svg{display:block;width:1.1em;height:1.1em}
.hub__row:hover .hub__go,.hub__row:focus-visible .hub__go{opacity:1;transform:translateX(5px)}
.hub__row:hover .hub__name{opacity:.62}
.hub__name{transition:opacity var(--t-fast) var(--ease)}
.hub__row:focus-visible{outline:2px solid var(--ink);outline-offset:4px}

/* ---------- motion work ----------
   Twelve pieces as a framed grid rather than the old carousel. The posters are
   now local files: they used to be hot-linked from Vimeo's CDN with hashed
   filenames, which silently break whenever a video is re-encoded. Each tile
   opens the piece on Vimeo, so the motion is one click away and nothing on
   this page depends on a third-party player loading. */
.mo{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(12px,1.6vw,22px);
}
.mo__tile{display:block;position:relative}
.frame--1x1{display:block;aspect-ratio:1/1}
.mo__tile img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.mo__tile::after{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(160deg,rgba(255,255,255,.16) 0%,rgba(255,255,255,0) 34%);
  opacity:0;transition:opacity var(--t-fast) var(--ease);
}
.mo__tile:hover::after,.mo__tile:focus-visible::after{opacity:1}
.mo__tile:focus-visible{outline:2px solid var(--ink);outline-offset:4px}
@media (max-width:900px){.mo{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:560px){.mo{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ---------- narrow ---------- */
@media (max-width:980px){
  .step{grid-template-columns:2.2em minmax(0,1fr);row-gap:10px}
  .step p{grid-column:2}
  .hub__row{grid-template-columns:2.2em minmax(0,1fr) auto;row-gap:12px}
  .hub__from{grid-column:3;grid-row:1/span 2;align-self:start}   /* beside the name; left to itself it dropped under the number */
  .hub__say{grid-column:2}
  .hub--plain .hub__say{grid-column:2/-1}
  .hub__go{display:none}
}
@media (max-width:720px){
  /* On a phone the four parts are cards in a row you swipe, like the team on
     About (Aston, 2026-09-30): the number at the top, the name and one line,
     then the price at the foot with the way in beside it. The last card snaps
     to the end so the row never stops on empty space. */
  .svc-prices .hub{
    display:grid;grid-auto-flow:column;grid-auto-columns:calc((100vw - 2 * var(--pad)) * .82);gap:12px;
    border-top:0;overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:none;
    margin-inline:calc(var(--pad) * -1);padding:0 var(--pad) 4px;scroll-padding-inline:var(--pad);
  }
  .svc-prices .hub::-webkit-scrollbar{display:none}
  .svc-prices .hub__row{
    position:relative;display:flex;flex-direction:column;scroll-snap-align:start;
    padding:20px 20px 22px;border:0;border-radius:22px;background:rgba(var(--ink-rgb),.05);
  }
  .svc-prices .hub__row:last-child{scroll-snap-align:end}
  .svc-prices .hub__name{margin-top:34px;font-size:27px;line-height:1.08}
  .svc-prices .hub__say{margin-top:12px;max-width:none;line-height:1.5}
  .svc-prices .hub__from{margin-top:auto;padding:30px 56px 0 0;text-align:left}
  .svc-prices .hub__from b{font-size:24px}
  .svc-prices .hub__go{
    display:grid;place-items:center;position:absolute;right:20px;bottom:22px;
    width:42px;height:42px;border-radius:50%;background:var(--ink);color:var(--paper);opacity:1;
  }
  .svc-prices .hub__go svg{width:18px;height:18px}
  .svc-prices .hub__row--whole{background:var(--ink);color:var(--paper);margin-bottom:0}
  .svc-prices .hub__row--whole::before{display:none}
  .svc-prices .hub > .hub__row--whole .hub__go{background:var(--paper);color:var(--ink);opacity:1}
  .svc-prices .hub__row:hover .hub__go{transform:none}
}
@media (max-width:640px){
  .plan__head{grid-template-columns:1fr}
  .plan__fig{text-align:left}
  .plan--lead{padding-left:16px}
  .shero__title{max-width:none}
}

/* ---------- the story voice ----------
   Paragraphs that read like someone talking, with the words that matter
   picked out the way the case studies' ledes do (work.css): a green
   highlighter, italics for the aside, bold for the hard fact. Used in the
   hero's lede, the section ledes and the price. */
.shero__lede mark,.sec__head .lede mark,.say mark,.entry__use mark,.shero__title mark{
  background:var(--green);color:#0A0A0C;
  padding:0 .08em;margin:0 -.02em;
  -webkit-box-decoration-break:clone;box-decoration-break:clone;
}
.shero__lede em,.sec__head .lede em,.say em,.entry__use em{font-style:italic}
/* purple, the main colour, beside green, the accent (Aston, 2026-09-29):
   <mark class="purple">, white on it for contrast */
:is(.shero__lede,.sec__head .lede,.say,.entry__use,.shero__title) mark.purple{background-color:var(--purple);color:#fff}
.say strong{font-weight:700}
.say{
  max-width:30ch;
  font-size:clamp(28px,3.3vw,50px);font-weight:300;
  letter-spacing:-.035em;line-height:1.32;text-wrap:pretty;   /* room above a highlighted line, clear of the line over it (Aston, 2026-09-30) */
}
.say a{text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.12em}
.say--more{max-width:40ch;margin-top:clamp(18px,2vw,28px);font-size:clamp(21px,2vw,30px);line-height:1.3;letter-spacing:-.02em}

/* ---------- the price ----------
   Two sentences and the way in, in a band of its own the width of the page:
   the page's own colours turned over, so it is light on the dark theme and
   dark on the light one, and the green highlight reads on either. */
.price{
  margin-top:var(--s-sec);padding-block:clamp(64px,8vw,120px);
  background:var(--ink);color:var(--paper);
  --line:rgba(var(--paper-rgb),.2);
}
.price__in > .label{display:inline-flex;align-items:center;gap:12px}
.price .mark{color:var(--paper)}
.price .say:first-of-type{margin-top:clamp(14px,1.4vw,22px)}   /* close under its label (Aston, 2026-09-30: too much space between the glyph and the line) */
.price__cta{display:inline-block;margin-top:clamp(24px,2.8vw,40px);font-size:var(--fs-body)}

/* ---------- a gold piece beside a section's heading ----------
   The growth arrow beside "Organic reach is our number one goal.": the same
   figure, float, pool and live hand-over as the closing pieces
   (.contact__gold), in a third column of the header. To show it bigger,
   change --gold-w; the live piece is geometry, the still is sharp to about
   430px wide (docs/gold/README.md). */
.sec__head--gold{grid-template-columns:auto minmax(0,1fr) auto;column-gap:clamp(24px,4vw,64px)}
.sec__head--gold .h2,.sec__head--gold .lede{grid-column:1 / 3}
.contact__gold--arrow{--gold-w:clamp(150px,17vw,250px)}
.sec__gold{grid-column:3;grid-row:1 / span 3;align-self:center;justify-self:end;width:var(--gold-w)}
@media (max-width:820px){
  .sec__head--gold{grid-template-columns:minmax(0,1fr) auto}
  .sec__head--gold .h2,.sec__head--gold .lede{grid-column:1 / -1}
  .sec__gold{grid-column:2;grid-row:1;justify-self:end;align-self:end;width:var(--gold-w)}
  .contact__gold--arrow{--gold-w:min(30vw,130px)}
}

/* ---------- the month in four ----------
   Four short columns, each under its own rule, rather than numbered rows:
   a number, a title, a sentence or two. Two by two on a tablet, one after
   another on a phone. */
.steps--four{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:36px clamp(20px,2.6vw,40px);border-top:0;
}
.steps--four .step{display:flex;flex-direction:column;gap:14px;padding:20px 0 0;border-bottom:0;border-top:var(--hair)}
.steps--four .step p{max-width:none}
@media (max-width:980px){.steps--four{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:560px){.steps--four{grid-template-columns:1fr;row-gap:28px}}

/* ---------- the platform logos, heaped on the ticker ----------
   Five app tiles resting on the ticker's top rule (js/drop.js). Without the
   script they sit where --x, --y and --r put them (tile widths from the
   right end of the rule and above it, radians); with it, they fall from
   above the top of the screen once the ticker is on screen, and they are
   placed by transform from the rule's left end. The head script marks the page
   drop-live so the heap does not show before it falls; if the script never
   runs, the heap appears after three seconds anyway. */
.rail-host{position:relative}
.drop{position:absolute;left:0;right:0;top:0;height:0;z-index:30;pointer-events:none;--t:clamp(46px,4.8vw,76px)}
.drop__tile{
  position:absolute;width:var(--t);height:var(--t);
  left:calc(100% + var(--x) * var(--t) - var(--t) / 2);
  top:calc(var(--y) * var(--t) - var(--t) / 2);
  transform:rotate(calc(var(--r) * 1rad));
  border-radius:22%;overflow:hidden;
  box-shadow:0 12px 22px -10px rgba(0,0,0,.5),0 2px 5px rgba(0,0,0,.16);
  pointer-events:auto;cursor:grab;touch-action:none;user-select:none;-webkit-user-select:none;
}
.drop__tile svg,.drop__tile img{display:block;width:100%;height:100%;-webkit-user-drag:none}
.drop.is-live .drop__tile{left:0;top:0;will-change:transform}
.drop.is-waiting .drop__tile{visibility:hidden}
/* the word by the cursor on a mouse (js/drop.js): "Interact" over a tile,
   Throw, Stack, Drop or Play around while one is held; styled with the
   homepage's in css/home.css (.cursor-tag) */
.drop__tile.is-held{cursor:grabbing}
.drop-live .drop__tile{visibility:hidden;animation:drop-failsafe 0s 3s forwards}
@keyframes drop-failsafe{to{visibility:visible}}
@media (prefers-reduced-motion:reduce){.drop__tile{cursor:default}}

/* (the tiles are gold now, their own edge catching the light, so the
   hairline that held the black and white tiles' shape is gone) */

/* ---------- the questions, as an accordion ----------
   Native <details>: no script, keyboard and screen readers for free. A row
   per question, ruled like the steps, a plus that turns to a minus; the
   answer opens under the question's words. Where the browser can animate
   to its natural height it slides open, elsewhere it simply opens. */
.sec__head:has(+ .faq){border-bottom:0}
.faq{border-top:var(--hair)}
.faq__item{border-bottom:var(--hair);interpolate-size:allow-keywords}
.faq__item summary{
  display:grid;grid-template-columns:2.6em minmax(0,1fr) auto;gap:clamp(16px,3vw,48px);align-items:center;
  padding:24px 0;cursor:pointer;list-style:none;
}
.faq__item summary::-webkit-details-marker{display:none}
.faq__item summary:focus-visible{outline:2px solid currentColor;outline-offset:6px}
.faq__q{font-size:var(--fs-h3);font-weight:400;letter-spacing:-.02em;text-wrap:balance;transition:opacity var(--t-fast) var(--ease)}
.faq__item summary:hover .faq__q{opacity:.72}
.faq__icon{position:relative;width:16px;height:16px;margin-right:4px}
.faq__icon::before,.faq__icon::after{
  content:"";position:absolute;left:0;right:0;top:50%;height:1.5px;margin-top:-.75px;
  background:currentColor;transition:transform .3s var(--ease);
}
.faq__icon::after{transform:rotate(90deg)}
.faq__item[open] .faq__icon::after{transform:rotate(0deg)}
.faq__item p{
  padding:0 0 28px calc(2.6em + clamp(16px,3vw,48px));max-width:calc(60ch + 6em);
  font-size:var(--fs-body);opacity:.7;line-height:1.6;
}
.faq__item::details-content{block-size:0;overflow:hidden;transition:block-size .35s var(--ease),content-visibility .35s allow-discrete}
.faq__item[open]::details-content{block-size:auto}
@media (prefers-reduced-motion:reduce){.faq__item::details-content{transition:none}}
@media (max-width:980px){
  .faq__item summary{grid-template-columns:2.2em minmax(0,1fr) auto;gap:16px}
  .faq__item p{padding-left:calc(2.2em + 16px)}
}

/* ---------- the splits on the service pages (Aston, 2026-09-28) ---------- */
@media (min-width:900px){
  /* the growth arrow's head: the heading on the left, the arrow and the line on the right */
  .sec__head--gold > .h2{grid-row:2;align-self:start}
  .sec__head--gold > .lede{grid-row:3}
  .sec__head--gold .sec__gold{grid-column:2;grid-row:2;justify-self:start;align-self:end;margin-bottom:clamp(16px,2vw,28px)}
  /* the price: the statement on the left, what the month holds and the way in on the right */
  .price__in{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);column-gap:clamp(48px,6vw,120px);align-items:start}   /* both columns start under the label; aligned to the end, a taller right column pushed the statement down */
  .price__in > .label{grid-column:1 / -1;justify-self:start}
  .price__in > h2.say{grid-column:1;grid-row:2 / span 2;max-width:none}
  .price .price__in > p.say--more{grid-column:2;grid-row:2;max-width:none;margin-top:clamp(22px,2.1vw,30px);font-size:clamp(19px,1.6vw,24px)}   /* a little lower than the statement, so the two first lines' capitals line up */
  .price__in > .price__cta{grid-column:2;grid-row:3}
  /* under the reach board: the sentence's end on the left, the scale of it and the way in on the right */
  .reach__end{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);column-gap:clamp(48px,6vw,120px);align-items:center;padding-top:clamp(20px,2.4vw,40px)}
  .reach__end .reach__scale{margin-top:0}
}

/* ---------- the case studies on the organic social page ----------
   Every row there is a reel, so the stage is taller, 4:5, to show more of
   it, and starts higher, beside the heading, in the space the heading leaves
   (Aston, 2026-09-28). Stacked on a small screen as everywhere else. */
.work--reels .stage .frame{aspect-ratio:4/5;width:min(100%,calc((100svh - var(--nav-h) - 96px) * 4 / 5))}
@media (min-width:901px){.work--reels .stage{margin-top:calc(var(--s-block) * -1.6)}}
/* The raised stage climbs into the header's row, so the header keeps to the
   rows' column; a long heading (a city page's) wraps instead of running under
   the stage. Percent padding reads the section's width, as the grid does. */
@media (min-width:1181px){.work--reels > .sec__head{padding-right:calc(36% + clamp(32px,5vw,80px))}}
@media (min-width:901px) and (max-width:1180px){.work--reels > .sec__head{padding-right:calc(40% + clamp(32px,5vw,80px))}}

/* ---------- the location pages: where we work ----------
   The heading on the left, the places as one run of type on the right, and
   the other cities and service beneath them (scripts/local/build.js). */
.areas__list{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:6px 0;font-size:var(--fs-lede);font-weight:300;letter-spacing:-.015em;line-height:1.45}
.areas__list li{display:inline-flex;align-items:center}
.areas__list li + li::before{content:"";width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.35;margin:0 14px}
.areas__also{margin-top:clamp(18px,2vw,28px);font-size:var(--fs-body);opacity:.72;line-height:1.6}
.areas__head > .label{display:inline-flex;align-items:center;gap:12px}
.areas__head .h2{margin-top:var(--s-row)}
.areas__body{margin-top:var(--s-block)}
@media (min-width:900px){
  .areas{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);column-gap:clamp(48px,7vw,140px);align-items:end}
  .areas__body{margin-top:0}
}

/* ---------- the short hero ----------
   A hero with no picture (the contact page) gives up the height the sheet
   needs, sets its lede beside the headline, as the section heads do, and the
   section after it starts closer, so the calendar is in reach. */
.shero--short{min-height:0}
.shero--short + .sec{padding-top:clamp(56px,7vw,112px)}
/* /work: the list is the page, so it starts close under the words */
.shero--short + .work--all{padding-top:clamp(24px,3.5vw,56px)}
@media (min-width:900px){
  .shero--short{display:grid;grid-template-columns:minmax(0,2fr) minmax(0,1fr);column-gap:clamp(48px,6vw,120px);align-items:end}
  .shero--short > .shero__eyebrow{grid-column:1 / -1;justify-self:start}
  .shero--short > .shero__title{grid-column:1;max-width:none}
  .shero--short > .shero__lede{grid-column:2;margin-top:0;padding-bottom:.4em}
}

/* ---------- the about page: on to the services ----------
   One section in place of the five-service list and "Where we work"
   (2026-09-30): the line, the way in and the city pages' small print on the
   left, Brendan's mock-ups fading one into the next on the right
   (js/showcase.js), the client's name under them. */
.explore{display:grid;gap:clamp(36px,4.4vw,72px);align-items:center}
@media (min-width:900px){
  .explore{grid-template-columns:minmax(0,5fr) minmax(0,7fr);grid-template-rows:auto auto;row-gap:0}
  .explore__copy{grid-column:1;grid-row:1;align-self:end}
  .explore__show{grid-column:2;grid-row:1 / span 2}
  .explore__near{grid-column:1;grid-row:2;align-self:start}
}   /* on a phone the small print follows the pictures, not the button */
.explore__copy > .label{display:inline-flex;align-items:center;gap:12px}
.explore__btn{gap:.45em}
.explore__title{margin-top:clamp(14px,1.4vw,22px);max-width:12ch;text-wrap:balance}
.explore__say{margin-top:clamp(16px,1.8vw,24px);max-width:34ch;font-size:var(--fs-lede);font-weight:300;line-height:1.45;letter-spacing:-.015em;opacity:.78}
.explore__near{margin-top:clamp(0px,3.2vw,48px);max-width:44ch;font-size:13px;line-height:1.65;opacity:.62}
.explore__near a{color:inherit;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.2em}
.explore__near a:hover{opacity:.8}
.explore__show{display:block;color:inherit;text-decoration:none}
.explore__frame{display:block;aspect-ratio:16/10}
.explore__frame img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transform:scale(1.015);
  transition:opacity 1.1s var(--ease),transform 3.8s var(--ease-out);
}
.explore__frame img.is-on{opacity:1;transform:none}   /* each settles as it arrives */
.explore__show:hover .explore__frame img.is-on{transform:scale(1.02);transition:opacity 1.1s var(--ease),transform .8s var(--ease-out)}
.explore__cap{display:flex;justify-content:space-between;gap:12px;padding-top:12px;opacity:.62}
@media (prefers-reduced-motion:reduce){.explore__frame img{transition:none;transform:none}}

/* ---------- the about page: the reel ----------
   The showreel edge to edge at the top (js/about-reel.js), its still
   underneath until the player plays. The box keeps 16:9 until that would
   run past the screen, then crops top and bottom. Two controls in the
   bottom corners, never over the reel's own words in the middle:
   play/pause in dark glass on the left, the sound on the right, green
   while it is off so it is the first thing found, glass once it is on. */
.ahero{position:relative;width:100%;background:#0A0A0C;overflow:hidden}
.ahero__media{position:relative;width:100%;aspect-ratio:16/9;max-height:88svh;overflow:hidden}
.ahero__poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.ahero__media video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;opacity:0;transition:opacity .8s var(--ease-out);pointer-events:none}
.ahero.is-live .ahero__media video{opacity:1}
.ahero.is-failed .ahero__ui{display:none}
.ahero__ui{position:absolute;left:0;right:0;bottom:clamp(16px,2.4vw,32px);display:flex;justify-content:space-between;align-items:center;padding-inline:var(--pad);pointer-events:none}
.ahero__btn{
  pointer-events:auto;display:inline-flex;align-items:center;justify-content:center;gap:10px;height:48px;
  margin:0;border:0;border-radius:999px;cursor:pointer;font:inherit;font-size:14px;font-weight:600;letter-spacing:-.01em;
  -webkit-tap-highlight-color:transparent;transition:background-color .2s,color .2s,transform .2s var(--ease-out);
}
.ahero__btn:active{transform:scale(.96)}
.ahero__btn:focus-visible{outline:2px solid #fff;outline-offset:3px}
.ahero__toggle,.ahero__sound.is-on{
  color:#fff;background:rgba(10,10,12,.5);
  -webkit-backdrop-filter:blur(14px) saturate(160%);backdrop-filter:blur(14px) saturate(160%);
}
.ahero__toggle{width:48px}
.ahero__i{width:20px;height:20px;fill:currentColor;flex:none}
.ahero__i--pause,.ahero.is-playing .ahero__i--play{display:none}
.ahero.is-playing .ahero__i--pause{display:block}
.ahero__sound{padding:0 20px 0 16px;background:var(--green);color:#0A0A0C;box-shadow:0 10px 28px -12px rgba(0,255,72,.7)}
.ahero__sound .ahero__waves,.ahero__sound.is-on .ahero__x{display:none}
.ahero__sound.is-on{box-shadow:none}
.ahero__sound.is-on .ahero__waves{display:inline}
@media (max-width:620px){
  .ahero__ui{bottom:12px}
  .ahero__btn{height:38px;font-size:13px}
  .ahero__toggle{width:38px}
  .ahero__sound{padding:0 14px 0 11px}
  .ahero__i{width:17px;height:17px}
}
@media (hover:hover){
  .ahero__toggle:hover,.ahero__sound.is-on:hover{background:rgba(10,10,12,.66)}
  .ahero__sound:not(.is-on):hover{background:#3dff72}
}

/* ---------- the reel, scrolled past (desktop) ----------
   js/about-reel.js moves the playing video into a small window in the
   bottom right once the hero has gone by: the same two controls, smaller,
   a close in the corner, and the picture itself a way back to the top. It
   keeps clear of the spine when that is docked at the bottom or right. */
.apip{
  position:fixed;right:var(--gap);bottom:var(--gap);z-index:90;
  width:clamp(280px,22vw,380px);aspect-ratio:16/9;border-radius:12px;overflow:hidden;
  background:#0A0A0C;box-shadow:0 26px 60px -24px rgba(0,0,0,.55),0 6px 18px -8px rgba(0,0,0,.35);
  opacity:0;transform:translateY(18px) scale(.96);pointer-events:none;
  transition:opacity .35s var(--ease-out),transform .45s var(--ease-out);
}
.apip.is-on{opacity:1;transform:none;pointer-events:auto}
@media (max-width:1240px){:root[data-nav-edge="bottom"] .apip{bottom:calc(var(--gap) + 58px + 16px)}}
:root[data-nav-edge="right"] .apip{right:calc(var(--gap) + 58px + 16px)}
.apip__media,.apip__media video{position:absolute;inset:0;width:100%;height:100%}
.apip__media video{object-fit:cover;display:block}
.apip__back{position:absolute;inset:0;margin:0;padding:0;border:0;background:transparent;cursor:pointer}
.apip__back:focus-visible{outline:2px solid #fff;outline-offset:-4px;border-radius:12px}
.apip__ui{position:absolute;left:10px;right:10px;bottom:10px;display:flex;justify-content:space-between;align-items:center;pointer-events:none}
.apip .ahero__btn{height:34px;font-size:12px;gap:6px}
.apip .ahero__toggle{width:34px}
.apip .ahero__sound{padding:0 12px 0 10px}
.apip .ahero__i{width:15px;height:15px}
.apip .ahero__i--pause,.apip.is-playing .ahero__i--play{display:none}
.apip.is-playing .ahero__i--pause{display:block}
.apip__close{
  position:absolute;top:8px;right:8px;width:28px;height:28px;display:grid;place-items:center;
  margin:0;padding:0;border:0;border-radius:50%;cursor:pointer;color:#fff;
  background:rgba(10,10,12,.5);-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);
  transition:background-color .2s;
}
.apip__close svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round}
.apip__close:hover{background:rgba(10,10,12,.72)}
.apip__close:focus-visible{outline:2px solid #fff;outline-offset:2px}
@media (prefers-reduced-motion:reduce){.apip{transition:none;transform:none}}

/* ---------- the about page: who we are ----------
   Everything on one left edge: the label, the promise, its footnote (the
   word we are named for), then the people. The superscript in the
   headline points at the footnote, so the two read as one thing. */
.intro{padding-top:clamp(64px,8vw,120px)}
.intro__eyebrow{display:inline-flex;align-items:center;gap:12px}
.intro__title{
  margin:clamp(18px,2vw,26px) 0 0;max-width:15ch;
  font-size:clamp(46px,7.4vw,112px);font-weight:700;letter-spacing:-.045em;line-height:.95;text-wrap:balance;
}
/* the highlighter on the letters, not the whole line box */
.intro__title mark{background:linear-gradient(var(--purple),var(--purple)) 0 70% / 100% 76% no-repeat;color:#fff;padding:0 .05em;margin:0 -.01em}
.intro__fn{margin-left:.06em;font-size:.26em;font-weight:500;letter-spacing:0;vertical-align:2.1em;color:inherit;text-decoration:none;opacity:.6}
.intro__fn:hover,.intro__fn:focus-visible{opacity:1}
.intro__note{margin:clamp(20px,2.4vw,32px) 0 0;max-width:56ch;font-size:var(--fs-lede);font-weight:300;line-height:1.5;letter-spacing:-.015em;opacity:.78}
.intro__note b{font-weight:700;letter-spacing:-.01em}
.intro__n{margin-right:.45em;font-size:.62em;font-weight:500;vertical-align:.55em;opacity:.7}
.intro .team__row{margin-top:clamp(56px,6vw,96px)}
/* under the team: a free 15-minute consult, and LinkedIn beside it (Aston,
   2026-10-01). One spoken line, then the button; on a desktop the button sits
   at the line's right, level with its last line. */
.team__cta{margin-top:clamp(44px,5vw,80px)}
.team__say{margin:0;max-width:24ch;font-size:clamp(26px,2.8vw,42px);font-weight:300;line-height:1.18;letter-spacing:-.03em;text-wrap:balance}
.team__say strong{font-weight:700}
.team__acts{display:flex;flex-wrap:wrap;align-items:center;gap:18px 30px;margin-top:clamp(22px,2.4vw,30px)}
.team__acts .consult__btn{margin-top:0}
.team__acts .link{font-size:var(--fs-body)}
@media (min-width:901px){
  .team__cta{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:clamp(32px,4vw,72px);align-items:end}
  .team__acts{margin-top:0;justify-content:flex-end}
}

/* ---------- the contact page: the call ----------
   What the call covers and the ways to reach us on the left, HighLevel's
   booking calendar on the right, in a panel that holds the calendar's height
   while it loads so nothing jumps. */
.book__side > .label{display:inline-flex;align-items:center;gap:12px}
.book__side .h2{margin-top:var(--s-row)}
.book__list{margin:clamp(22px,2.6vw,34px) 0 0;padding:0;list-style:none;font-size:var(--fs-lede);font-weight:300;line-height:1.45;letter-spacing:-.015em}
.book__list li{padding:12px 0;border-top:var(--hair)}
.book__list li:last-child{border-bottom:var(--hair)}
.book__ways{margin:clamp(28px,3vw,44px) 0 0;display:grid;gap:18px}
.book__ways div{display:grid;gap:6px}
.book__ways dt{opacity:.62}
.book__ways dd{margin:0;font-size:var(--fs-body)}
/* The calendar draws its own white, rounded, bordered card on a transparent
   page, so the panel draws nothing: a fill or border of ours showed as a lip
   and a second line around theirs. HighLevel's embed script sets the frame's
   height to the calendar's; the CSS height only stands in until it measures,
   and the loader is a plain white card of the same size meanwhile. */
.book__cal{position:relative;margin-top:var(--s-block)}
/* the call or a message, side by side behind a switch (js/consult.js) */
.book__talk{margin-top:var(--s-block)}
.book__talk .book__cal{margin-top:0}
.talk--page .talk__switch{margin-bottom:clamp(20px,2.4vw,28px)}
.talk--page .talk__panel--message{padding:0}
.book__cal iframe{display:block;width:100%;height:672px;background:transparent}
.book__loader{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;gap:12px;font-size:14px;color:#0A0A0C;background:#fff;border-radius:12px;z-index:1}
.book__loader[hidden]{display:none}
.book__spin{width:18px;height:18px;border-radius:50%;border:2px solid rgba(10,10,12,.15);border-top-color:#0A0A0C;animation:book-spin .8s linear infinite}
@keyframes book-spin{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){.book__spin{animation:none}}
@media (min-width:900px){
  .book{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);column-gap:clamp(48px,6vw,120px);align-items:start}
  .book__side{position:sticky;top:calc(var(--nav-h) + 48px)}
  .book__cal{margin-top:0}
  .book__talk{margin-top:0}
}

/* ---------- the about page: the name ----------
   The word set as a dictionary entry: the term large on the left, sized in
   container units so it fills its column at any width and never runs past
   it; the part of speech, the meaning and one line of voice on the right. */
.word > .label{display:inline-flex;align-items:center;gap:12px}
.word__in{margin-top:var(--s-block)}
.word__box{container-type:inline-size}
.word__term{margin:0;font-size:25cqw;font-weight:700;letter-spacing:-.055em;line-height:.8;padding-bottom:.06em}
.word__def{margin-top:clamp(24px,3vw,40px)}
.word__kind{opacity:.62}
.word__mean{margin:12px 0 0;font-size:clamp(34px,4.2vw,64px);font-weight:300;letter-spacing:-.04em;line-height:1}
.word__def .say--more{margin-top:clamp(22px,2.4vw,34px)}
@media (min-width:900px){
  .word__in{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);column-gap:clamp(48px,6vw,120px);align-items:end}
  .word__def{margin-top:0}
}

/* the hub without prices (the about page): the rows lose the price column */
.hub--plain .hub__row{grid-template-columns:2.6em minmax(0,1.05fr) minmax(0,1.5fr) 1.4em}
@media (max-width:980px){.hub--plain .hub__row{grid-template-columns:2.2em minmax(0,1fr)}}
.sec__head .lede .link{white-space:nowrap}

/* ---------- the 404 page ----------
   The 404 as type, with the brand's glyph for the zero; the glyph is a
   button that turns to the next glyph and the next excuse (js/404.js). The
   text sits beside it on a wide screen and under it on a phone. Then the
   places people meant, in three ruled columns. */
.lost{padding-top:calc(var(--nav-h) + clamp(30px,6vh,80px))}
.lost__code{
  display:inline-flex;align-items:center;gap:.02em;
  margin:0;padding:0;border:0;background:none;color:inherit;font:inherit;cursor:pointer;
  font-size:clamp(120px,22vw,340px);font-weight:700;letter-spacing:-.06em;line-height:.78;
  -webkit-tap-highlight-color:transparent;
}
.lost__g{width:.68em;height:.68em;margin:0 .04em;color:var(--green);transition:transform .36s var(--ease)}
.lost__code:hover .lost__g{transform:rotate(-24deg)}
.lost__code.is-turning .lost__g{transform:rotate(90deg) scale(.6)}
.lost__code:focus{outline:none}
.lost__code:focus-visible{outline:2px solid var(--ink);outline-offset:8px;border-radius:12px}
.lost__text{margin-top:clamp(28px,4vw,56px)}
.lost__quip{margin:0;font-size:clamp(34px,4.4vw,68px);font-weight:400;letter-spacing:-.04em;line-height:1.02;text-wrap:balance;max-width:16ch}
.lost__path{margin-top:clamp(18px,2vw,26px);display:flex;flex-wrap:wrap;align-items:baseline;gap:10px}
.lost__path .label{opacity:.62}
.lost__path code{font:400 15px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace;word-break:break-all}
.lost + .sec{padding-top:clamp(56px,7vw,112px)}
@media (min-width:900px){
  .lost{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);column-gap:clamp(48px,6vw,120px);align-items:end}
  .lost__text{margin-top:0;padding-bottom:.4em}
}
@media (prefers-reduced-motion:reduce){.lost__g{transition:none}}

.dest{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));column-gap:clamp(32px,4vw,64px);row-gap:var(--s-block)}
.dest__title{display:block;margin:0 0 10px;opacity:.62}
.dest__row{
  display:grid;grid-template-columns:minmax(0,1fr) 1.2em;column-gap:14px;align-items:baseline;
  padding:14px 0;border-bottom:var(--hair);color:inherit;text-decoration:none;
}
.dest__name{font-size:clamp(19px,1.6vw,24px);font-weight:400;letter-spacing:-.02em;transition:opacity var(--t-fast) var(--ease)}
.dest__say{grid-column:1;margin-top:4px;font-size:14px;opacity:.6}
.dest__go{grid-column:2;grid-row:1;opacity:.4;transition:transform var(--t-fast) var(--ease),opacity var(--t-fast) var(--ease)}
.dest__row:hover .dest__name{opacity:.62}
.dest__row:hover .dest__go,.dest__row:focus-visible .dest__go{opacity:1;transform:translateX(4px)}
.dest__row:focus-visible{outline:2px solid var(--ink);outline-offset:4px}
@media (max-width:900px){.dest{grid-template-columns:1fr}}

/* ---------- the about page: the team ----------
   Three portraits on the same dark ground with the faces level across the
   row; the name, then the role as a label with the person's shape. Each
   portrait is two layers, the ground (the frame's background) and the
   person cut out, and between them wait the things that person loves: at
   rest they are small and hidden behind the head; hovered, or tapped
   (js/team.js), they spring out one after another and settle round it,
   then float. --x/--y are where each settles (% of the frame), --s its size
   (share of the width), --r its tilt, --d its delay. On a phone the row is
   a strip that snaps: one portrait and a glimpse of the next
   (scripts/local/build.js). */
/* The things may leave the photograph (Aston, 2026-09-29): the frame does
   not clip, the open portrait lifts above its neighbours so its things pass
   over them, and the section clips only sideways, so nothing ever makes the
   page scroll. */
.team{overflow-x:clip}
.team__row{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(16px,2.4vw,40px)}
.team__item,.team__fig{margin:0}
.team__item{position:relative;z-index:0}
.team__item:hover,.team__item.is-open{z-index:3}
.team__frame{
  position:relative;display:block;width:100%;aspect-ratio:4/5;overflow:visible;
  margin:0;padding:0;border:0;border-radius:0;font:inherit;color:inherit;cursor:pointer;
  background:#0A0A0C url(/assets/images/team/ground.webp?v=cef3bf1c) center / cover no-repeat;
  container-type:inline-size;-webkit-tap-highlight-color:transparent;
}
.team__cut{position:absolute;inset:0;z-index:2;display:block;width:100%;height:100%;object-fit:cover}
.team__things{position:absolute;inset:0;z-index:1}
.team__thing{
  position:absolute;left:calc(var(--x) * 1%);top:calc(var(--y) * 1%);width:calc(var(--s) * 100%);height:auto;aspect-ratio:1;
  /* at rest: behind the head, small and unseen */
  transform:translate(-50%,-50%) translate(calc((50 - var(--x)) * 1cqw),calc((40 - var(--y)) * 1.25cqw)) rotate(0deg) scale(.3);
  opacity:0;
  transition:transform .42s cubic-bezier(.55,0,.75,.2),opacity .18s linear .24s;
  will-change:transform;
}
.team__item.is-open .team__thing{
  transform:translate(-50%,-50%) translate(0,0) rotate(calc(var(--r) * 1deg)) scale(1);
  opacity:1;
  transition:transform .8s cubic-bezier(.3,1.5,.5,1) var(--d),opacity .15s linear var(--d);
  animation:team-float 3.6s ease-in-out calc(var(--d) + .8s) infinite alternate;
}
@media (hover:hover) and (pointer:fine){
  .team__item:hover .team__thing{
    transform:translate(-50%,-50%) translate(0,0) rotate(calc(var(--r) * 1deg)) scale(1);
    opacity:1;
    transition:transform .8s cubic-bezier(.3,1.5,.5,1) var(--d),opacity .15s linear var(--d);
    animation:team-float 3.6s ease-in-out calc(var(--d) + .8s) infinite alternate;
  }
}
@keyframes team-float{
  from{translate:0 0}
  to{translate:0 -3.5%}
}
/* the live version (js/team3d-scene.js) draws them in 3D on a canvas in the
   same place, and the stills stand down */
/* the canvas reaches past the frame, 30% of its width each side and 20% of
   its height above and below (BX, BY in js/team3d-scene.js), so the live
   things can leave it too */
.team__canvas{position:absolute;left:-30%;top:-20%;z-index:1;width:160%;height:140%;display:block;pointer-events:none}
.team__frame.is-3d .team__things{visibility:hidden}
/* a second photo under a portrait (Aston's): js/team.js grains the headshot
   away into it on hover or a tap, on a canvas above the portrait; his things
   come out over it, and his caption changes with it (below). */
.team__grain{position:absolute;inset:0;z-index:3;display:block;width:100%;height:100%;pointer-events:none}
.team__frame--alt .team__things,.team__frame--alt .team__canvas{z-index:4}
/* Aston's caption changes with his photo (Aston, 2026-09-30, in place of
   the sticky note): the role and "Lake Tahoe with the fam" share one place
   and cross-fade, so nothing around them moves */
.team__swap{display:inline-grid}
.team__swap > span{grid-area:1/1;transition:opacity .35s var(--ease)}
.team__swap-b{opacity:0}
.team__fig.is-alt .team__swap-a{opacity:0}
.team__fig.is-alt .team__swap-b{opacity:1}
.team__cap{display:flex;flex-direction:column;align-items:flex-start;gap:10px;padding-top:18px}
.team__name{font-size:var(--fs-h3);letter-spacing:-.02em;line-height:1.1}
.team__role{display:inline-flex;align-items:center;gap:10px;opacity:.72}
@media (prefers-reduced-motion:reduce){
  .team__thing,.team__item.is-open .team__thing,.team__item:hover .team__thing{
    transform:translate(-50%,-50%) rotate(calc(var(--r) * 1deg));animation:none;transition:opacity .2s linear;
  }
}
@media (max-width:720px){
  /* a scroller clips; the padding above and below gives the things room to leave the photo.
     The end holds room for the last person's things too (Aston, 2026-09-29): they reach
     about 14% past the card, and without it opening them grew the scroller, which
     re-snapped and pulled the card sideways. The cards stay 78% of the page's width. */
  .team__row{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:calc((100vw - 2 * var(--pad)) * .78);overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;margin-inline:calc(var(--pad) * -1);padding-inline:var(--pad) calc(var(--pad) + (100vw - 2 * var(--pad)) * .11);scroll-padding-inline:var(--pad);scrollbar-width:none;
    padding-block:48px 12px;margin-block:-48px -12px}
  .team__row::-webkit-scrollbar{display:none}
  .team__item{scroll-snap-align:start}
}

/* (the gold pieces sat in the heroes on a desktop for a day, 2026-09-29;
   Aston moved them back to the close of each service page, 2026-09-30) */

/* ---------- client logos in the hero ----------
   The strip under a service page's words (scripts/work/build.js): the
   clients' own marks in one ink, each drawn to about the same area, running
   slowly sideways with soft ends; it stops under the pointer, and stands
   still for reduced motion. It keeps to the left column, clear of the sheet. */
.logos{margin-top:clamp(30px,4vw,56px);width:min(100%,460px,32vw)}
.logos__label{opacity:.62}
.logos__track{
  display:flex;overflow:hidden;margin-top:16px;padding:6px 0;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);
}
.logos__row{
  flex:none;display:flex;align-items:center;gap:clamp(30px,3.2vw,48px);padding-right:clamp(30px,3.2vw,48px);
  animation:logos-run 46s linear infinite;
}
.logos:hover .logos__row,.logos:focus-within .logos__row{animation-play-state:paused}
.logos__item{flex:none;display:block;line-height:0}
.logos__item img{display:block;max-width:none;opacity:.62;transition:opacity .2s}
a.logos__item:hover img,a.logos__item:focus-visible img{opacity:1}
a.logos__item:focus-visible{outline:2px solid var(--ink);outline-offset:4px}
:root[data-theme="dark"] .logos__item img{filter:invert(1)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .logos__item img{filter:invert(1)}}
@keyframes logos-run{to{transform:translateX(-100%)}}
@media (prefers-reduced-motion:reduce){
  .logos__row{animation:none;flex-wrap:wrap;row-gap:18px}
  .logos__row[aria-hidden]{display:none}
  .logos__track{-webkit-mask-image:none;mask-image:none}
}
@media (max-width:820px){.logos{width:100%}}

/* ---------- the price band, with more than one price ----------
   The band as on /social-media-management, grown for the pages that sell
   more than one thing (Aston, 2026-09-29): the partners' marks under the way
   in (paid media), a row of the plans under the sentences, each with its
   chip, figure and one line (maintenance), and the terms as small print
   across the foot. */
.price__badges{margin-top:clamp(20px,2.4vw,28px)}
.price__tiers{
  margin-top:clamp(44px,5vw,80px);padding-top:clamp(24px,3vw,40px);border-top:1px solid var(--line);
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));column-gap:clamp(20px,3vw,48px);row-gap:36px;
}
.price__chip{display:block;width:min(100%,150px);height:auto;margin-bottom:18px}
.price__tier .label{opacity:.6}
.price__n{margin-top:8px;font-size:clamp(36px,3.8vw,60px);font-weight:300;letter-spacing:-.04em;line-height:1}
.price__per{margin-left:10px;font-size:12px;letter-spacing:.06em;text-transform:uppercase;opacity:.62;vertical-align:middle}
.price__tier > p:last-child{margin-top:14px;font-size:var(--fs-body);line-height:1.5;opacity:.72;max-width:36ch}
.price__fine{margin-top:clamp(28px,3.4vw,48px);font-size:13px;line-height:1.6;opacity:.62;max-width:96ch}
@media (min-width:900px){
  .price__in > .price__badges{grid-column:2;grid-row:4}
  .price__in > .price__tiers,.price__in > .price__fine{grid-column:1 / -1}
}
@media (max-width:899px){.price__tiers{grid-template-columns:1fr}}

/* ---------- the services page (Aston, 2026-09-30) ----------
   Managed Digital Marketing, the whole, in the dark band: under the price,
   what's inside it, four across on a rule; then the parts on their own
   (.hub), what clients say, and the five services in gold. */
.whole__in{
  list-style:none;margin:clamp(40px,5vw,72px) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:24px clamp(20px,2.6vw,40px);
}
.whole__in li{display:flex;flex-direction:column;gap:8px;padding-top:18px;border-top:1px solid rgba(var(--paper-rgb),.24)}
.whole__in a,.whole__in b{font-size:clamp(19px,1.6vw,24px);font-weight:400;letter-spacing:-.02em;line-height:1.2}
.whole__in a{align-self:flex-start;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:.2em;text-decoration-color:rgba(var(--paper-rgb),.4)}
.whole__in a:hover,.whole__in a:focus-visible{text-decoration-color:currentColor}
.whole__in span{font-size:var(--fs-body);line-height:1.5;opacity:.72}
@media (min-width:900px){.price__in > .whole__in,.price--whole .price__fine{grid-column:1 / -1}}
@media (max-width:980px){.whole__in{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:520px){.whole__in{grid-template-columns:1fr}}

/* the five in gold, the feature under the whole (Aston, 2026-09-30: "more
   prominent and maybe toward the top"): one shelf, in order of the size of
   the service, Managed Digital Marketing's @ first and the largest, each
   piece a way to its page; on a phone, a row to swipe. Two rows the items share
   (subgrid): every piece stands on one line and every name starts on the
   next, however many lines the names run to; the columns take each piece's
   own width and spread across the page. */
.five .shelf{
  display:grid;grid-template-columns:repeat(5,auto);justify-content:space-between;grid-auto-rows:auto;
  column-gap:12px;row-gap:0;margin-top:clamp(56px,6.4vw,104px);
}
.shelf__item{
  grid-row:span 2;display:grid;grid-template-rows:subgrid;justify-items:center;row-gap:clamp(22px,2.4vw,34px);
  color:inherit;text-decoration:none;border-radius:12px;text-align:center;
}
.shelf__item > figure{align-self:end}
.shelf__item > .label{align-self:start}
.shelf__item .contact__gold,.shelf__item .contact__monument{justify-self:auto;width:var(--w);margin:0}
.shelf__item--arrow{--w:clamp(110px,12.5vw,180px)}
.shelf__item--coin{--w:clamp(104px,11.8vw,170px)}
.shelf__item--at{--w:clamp(150px,17.4vw,250px)}
.shelf__item--monument{--w:clamp(118px,13.5vw,195px)}
.shelf__item--code{--w:clamp(128px,14.6vw,210px)}
.shelf__item > .label{max-width:17ch;line-height:1.45;text-wrap:balance;opacity:.72;transition:opacity var(--t-fast) var(--ease)}
.shelf__item:hover > .label,.shelf__item:focus-visible > .label{opacity:1}
.shelf__item--at > .label{opacity:1}
.shelf__item:focus-visible{outline:2px solid var(--ink);outline-offset:8px}
@media (max-width:820px){
  /* a row to swipe, not a stack (Aston, 2026-09-30): edge to edge, each
     piece snapping to the page's edge, with room above and below so the
     floating pieces and their shadows are not cut off by the scroller */
  .five .shelf{
    grid-template-columns:none;grid-template-rows:auto auto;grid-auto-flow:column;grid-auto-columns:max-content;
    justify-content:start;column-gap:clamp(24px,6vw,40px);
    overflow-x:auto;overscroll-behavior-x:contain;scroll-snap-type:x mandatory;scrollbar-width:none;
    margin:clamp(20px,4vw,32px) calc(var(--pad) * -1) 0;padding:28px var(--pad) 16px;scroll-padding-inline:var(--pad);
  }
  .five .shelf::-webkit-scrollbar{display:none}
  .shelf__item{row-gap:22px;scroll-snap-align:start}
  .shelf__item:last-child{scroll-snap-align:end}
  /* sized so the third piece peeks in at the edge: there is more to swipe */
  .shelf__item--at{--w:min(44vw,210px)}
  .shelf__item--coin{--w:min(30vw,140px)}
  .shelf__item--arrow{--w:min(32vw,150px)}
  .shelf__item--monument{--w:min(34vw,160px)}
  .shelf__item--code{--w:min(36vw,170px)}
}

/* ---------- what's inside Managed Digital Marketing (digital-marketing.html) ----------
   A spider (Aston, 2026-09-30): the whole in a disc in the middle, the four
   parts in gold at the corners, a line drawn out to each as it appears. A
   part is a button: a tap opens its price in a dark card under it
   (js/spider.js), one at a time. Without the script the cards simply show. */
.spider{position:relative;max-width:1060px;aspect-ratio:10/7.4;margin:clamp(48px,6vw,96px) auto clamp(80px,9vw,140px)}
.spider__lines{position:absolute;inset:0;width:100%;height:100%;z-index:0;overflow:visible}
.spider__lines line{stroke:rgba(var(--ink-rgb),.28);stroke-width:1.2;vector-effect:non-scaling-stroke}
.spider__lines{opacity:0;transition:opacity 1s var(--ease-out) .3s}
.spider.in .spider__lines{opacity:1}
.spider__lines--tall{display:none}
.spider__hub{
  position:absolute;left:50%;top:50%;z-index:1;transform:translate(-50%,-50%);
  width:27%;aspect-ratio:1;border-radius:50%;padding:3%;
  display:grid;place-content:center;gap:10px;text-align:center;
  background:var(--ink);color:var(--paper);box-shadow:0 30px 60px -30px rgba(var(--shadow-rgb),.55);
  transition:transform var(--t-med) var(--ease);
}
.spider__hub:hover,.spider__hub:focus-visible{transform:translate(-50%,-50%) scale(1.03)}
.spider__hub:focus-visible{outline:2px solid var(--ink);outline-offset:6px}
.spider__hub-name{font-size:clamp(17px,2.1vw,30px);font-weight:400;letter-spacing:-.03em;line-height:1.08;text-wrap:balance}
.spider__hub-price{font-size:11px;font-weight:500;letter-spacing:var(--track-label);text-transform:uppercase;opacity:.7}
.spider__node{position:absolute;z-index:2;width:30%;transform:translate(-50%,-50%)}
.spider__node--tl{left:17%;top:24%}.spider__node--tr{left:83%;top:24%}
.spider__node--bl{left:17%;top:76%}.spider__node--br{left:83%;top:76%}
.spider__part{
  display:flex;flex-direction:column;align-items:center;gap:12px;width:100%;margin:0;padding:10px 6px;
  border:0;border-radius:16px;background:none;color:inherit;font:inherit;cursor:pointer;text-align:center;
}
.spider__part:focus-visible{outline:2px solid var(--ink);outline-offset:4px}
.spider__part figure{margin:0;width:var(--w);justify-self:auto}
.spider__node--arrow{--w:62%}.spider__node--coin{--w:52%}.spider__node--monument{--w:60%}.spider__node--code{--w:72%}
/* the names sit on the page's own paper, so a line runs in behind them */
.spider__name,.spider__hint{background:var(--paper);border-radius:6px}
.spider__name{padding:2px 8px;margin-top:6px;font-size:clamp(15px,1.45vw,21px);font-weight:400;letter-spacing:-.015em;line-height:1.2;text-wrap:balance}
.spider__hint{padding:3px 8px;color:rgba(var(--ink-rgb),.62);transition:color var(--t-fast) var(--ease)}   /* colour, not opacity, so its paper stays solid */
.spider__part:hover .spider__hint,.spider__node.is-open .spider__hint{color:var(--ink)}
.spider__node.is-open .spider__hint{visibility:hidden}
/* the price card */
.spider__price{
  display:grid;gap:4px;margin:10px auto 0;width:max-content;max-width:280px;padding:14px 18px 16px;
  border-radius:14px;background:var(--ink);color:var(--paper);text-align:left;
  box-shadow:0 24px 50px -24px rgba(var(--shadow-rgb),.6);
}
/* a card can hold more than one price (maintenance and hosting) and a note
   (what paid media excludes; what web design quotes) */
.spider__row{display:grid;gap:3px;margin:0}
.spider__row + .spider__row{margin-top:10px}
.spider__price b{font-size:clamp(20px,1.7vw,26px);font-weight:400;letter-spacing:-.02em;line-height:1.15}
.spider__row > span{font-size:11px;font-weight:500;letter-spacing:var(--track-label);text-transform:uppercase;opacity:.7}
.spider__note{margin:8px 0 0;font-size:13px;line-height:1.45;opacity:.78;max-width:28ch}
.spider__price .link{justify-self:start;margin-top:8px;font-size:14px}
.spider.is-live .spider__price{
  position:absolute;left:50%;top:calc(100% - 4px);margin:0;z-index:5;
  opacity:0;visibility:hidden;transform:translate(-50%,6px);
  transition:opacity .2s var(--ease),transform .25s var(--ease),visibility 0s linear .25s;
}
.spider.is-live .spider__node.is-open .spider__price{opacity:1;visibility:visible;transform:translate(-50%,0);transition:opacity .2s var(--ease),transform .25s var(--ease),visibility 0s}
.spider__node.is-open{z-index:6}
@media (prefers-reduced-motion:reduce){.spider__lines{opacity:1;transition:none}}
@media (max-width:700px){
  .spider{aspect-ratio:10/13}
  .spider__lines--wide{display:none}
  .spider__lines--tall{display:block}
  .spider__hub{width:40%}
  .spider__node{width:46%}
  .spider__node--tl{left:25%;top:16%}.spider__node--tr{left:75%;top:16%}
  .spider__node--bl{left:25%;top:84%}.spider__node--br{left:75%;top:84%}
  .spider__hint{font-size:10px}
  /* the cards open inward, so they stay on the screen */
  .spider.is-live .spider__node--tl .spider__price,.spider.is-live .spider__node--bl .spider__price{left:0;transform:translate(0,6px)}
  .spider.is-live .spider__node--tr .spider__price,.spider.is-live .spider__node--br .spider__price{left:auto;right:0;transform:translate(0,6px)}
  .spider.is-live .spider__node--tl.is-open .spider__price,.spider.is-live .spider__node--bl.is-open .spider__price,
  .spider.is-live .spider__node--tr.is-open .spider__price,.spider.is-live .spider__node--br.is-open .spider__price{transform:translate(0,0)}
}
