/* ==========================================================================
   AMIN APAC, shared stylesheet
   Built on the AMIN Worldwide design system (MD/www.aminworldwide.com-DESIGN.md)

   Contents
   01. Design tokens
   02. Reset & base
   03. Typography
   04. Layout utilities
   05. Buttons & links
   06. Cards, tiles & tags
   07. Site header
   08. Site footer
   09. Masthead (dark plum hero, shared by profile page + slide-in panel)
   10. Stat bar
   11. Page: map + directory
   12. Page: member profile
   13. Slide-in profile panel
   14. Responsive
   15. Reduced motion
   ========================================================================== */


/* ==========================================================================
   01. Design tokens
   ========================================================================== */
:root{
  /* Brand purples */
  --purple:#904FFF;          /* Electric Purple, primary signature */
  --purple-deep:#7A00DF;     /* Deep Purple, focus and hover */
  --plum:#3A2364;            /* Deep Plum, dark surfaces and accents */
  --plum-rgb:58 35 100;      /* same colour, unpacked for scrubbed alpha */
  --lavender:#D3B9FF;        /* Light Lavender, soft accent */
  --pale:#F4EDFF;            /* Pale Purple, tinted surfaces */

  /* Neutrals */
  --black:#000000;
  --white:#FFFFFF;
  --slate:#212934;           /* body copy */
  --gray:#333333;            /* secondary text */
  --ash:#4A4E57;             /* placeholder / inactive */
  --offwhite:#F9F9FB;
  --mist:#EFEFF2;            /* neutral chip fill */
  --haze:#F4F4F7;            /* filled card surface */
  --line:#E2E2E2;
  /* Scrollbar thumb. One mid tone, because the panel's track runs over both the
     plum masthead and the white body and cannot change colour between them.
     Picked for the tightest of the two: 3.42:1 on white, 3.83:1 on plum, so it
     clears the 3:1 non-text contrast floor at both ends of the track. */
  --scroll-thumb:#8F86A8;

  /* The site's dark. It was written raw as #14161A in hero-map.css and
     footer.css before this token existed - the hero stage, the footer ground and
     now the neutral button are all one colour and should move together. */
  --ink:#14161A;
  --ink-rgb:20 22 26;       /* same colour, unpacked for scrubbed alpha */

  /* Semantic
     THE ERROR RED IS A THREE-VALUE SET, not one hue, because an error state is
     a surface, an ink and a line and they cannot be the same number. It is
     declared here rather than inside a component sheet for the reason
     tech-audit.css already wrote down while working around its absence:
     brief-form.css held #FDECEC and #8E1B1B as raw hex, tech-audit.css needed
     the same idea and could not reference them, so it mixed its own from --red
     and left a note saying the page must not end up with two reds meaning the
     same thing. Two sheets had already solved this separately. Now there is one
     set and every red on the site resolves to it.

       --red        the line. Borders and the thickened invalid outline.
       --red-bg     the surface. Only ever behind --red-ink.
       --red-ink    the text. 7.4:1 on --red-bg, which is what lets a message
                    carry meaning in words as well as in colour.

     COLOUR IS NEVER THE ONLY SIGNAL. base.css's standing rule: an invalid field
     carries a border AND a message that names the field, because a red box on
     its own is not readable to everybody who has to read it. */
  --green:#65BC7B;
  --red:#CF2E2E;
  --red-bg:#FDECEC;
  --red-ink:#8E1B1B;

  /* THE SIX DISCIPLINE HUES, moved here from network-graph.css because a second
     feature now uses them and a colour declared inside one component's sheet is
     a colour the next feature copies rather than reuses. network-graph.css's own
     reasoning for the set is left where it was written: six hues anchored on the
     brand purple, which goes to the largest cluster rather than to whichever
     discipline sounded most important, far enough apart on the wheel to be told
     apart at 10px, and warm/cool balanced so no half of a graph reads as louder
     than the other.

     THEY MEAN A DISCIPLINE, and any second use has to earn the rhyme rather than
     borrow the palette. The tools wall earns it by mapping each tool to the
     discipline it actually serves - the audit is the digital-and-performance
     tool, the launch check is the market-entry one - so a reader who has seen
     the constellation on index reads the wall correctly rather than being told
     the same colour means two things. Anything that cannot make that mapping
     honestly should not use these.

     Unpacked triples beside them, the same idiom as --plum-rgb, so a feature can
     scrub one back to a wash without a second declaration of the same colour. */
  --d-brand  :#904FFF;   --d-brand-rgb  :144  79 255;   /* Brand & campaigns     */
  --d-digital:#2F6BE3;   --d-digital-rgb: 47 107 227;   /* Digital & performance */
  --d-b2b    :#14897A;   --d-b2b-rgb    : 20 137 122;   /* B2B & market entry    */
  --d-content:#E8632F;   --d-content-rgb:232  99  47;   /* Content & production  */
  --d-pr     :#E0407A;   --d-pr-rgb     :224  64 122;   /* PR & public affairs   */
  --d-media  :#F0A81E;   --d-media-rgb  :240 168  30;   /* Media & activation    */

  /* THE THREE EDITION HUES, and they are a SECOND SET rather than three more of
     the set above. --d-* has exactly six members because there are exactly six
     disciplines; the newsletter's other three categories - the watchlist, the
     tools, the network - are not disciplines and must not be filed as though
     they were, or the mapping the six are declared for stops being true.

     THEY SHARED --plum FOR A DAY AND THAT WAS WRONG (2026-08-31). Three cards
     in one colour beside six in six colours does not read as "these three are a
     different kind of thing"; it reads as three cards whose colour did not get
     set. A category that is worth listing separately is worth telling apart.

     PICKED FROM THE GAPS, NOT INVENTED. The six sit at 17, 39, 172, 220, 262
     and 338 degrees, which leaves exactly three clean spaces on the wheel -
     green, cyan and magenta. The tightest gap the three create is 22 degrees,
     which is the same as the tightest gap the six already had between content
     and media, so nothing here is harder to tell apart than what shipped. Both
     saturation and lightness sit inside the existing set's range, so the nine
     read as one family rather than six plus three.

     THE MAPPING IS NOT ARBITRARY. Cyan is the signal colour and the watchlist
     is a forward signal; green passes and the tools return a reading; magenta
     is the neighbour of the brand violet and the network IS the brand. */
  --e-watch  :#0F9BC7;   --e-watch-rgb  : 15 155 199;   /* What we're watching   */
  --e-tools  :#1BA85C;   --e-tools-rgb  : 27 168  92;   /* New free tools        */
  --e-network:#C733D9;   --e-network-rgb:199  51 217;   /* The network           */

  /* Globe, the world is cool blue so that purple means one thing on the map:
     a member is here. Blue sits next to purple on the wheel, so the field stays
     harmonious with the brand instead of competing with it. */
  --globe-land:#C3C8D6;      /* rest of the world */
  --globe-apac:#6E93D6;      /* the region, lifted out of it */
  --globe-ocean:#E9EDF6;
  --globe-grat:#DBE3F1;      /* graticule */
  --globe-limb:#E1E5EE;
  /* Selection. The dots rest in green, so the selected one takes the brand
     purple, which is also what its card, panel and tile are painted in, so
     opening a city and landing on its card read as one colour. */
  --pin-active:#0A8A4F;
  --pin-active-ink:#064E2C;  /* selected label - darker than hover, and bold */
  --pin-hover-ink:#0B6B3E;   /* hover label, echoing the green pin */

  /* The longitude ramp: deep plum in the west to electric purple in the east.
     Endpoints are all sanctioned brand purples. */
  --ramp:linear-gradient(90deg,#3A2364,#7A00DF,#904FFF);

  /* Spacing, 4px base scale */
  --s1:4px;   --s2:8px;   --s3:12px;  --s4:16px;  --s5:20px;  --s6:24px;
  --s7:28px;  --s8:32px;  --s9:36px;  --s10:40px; --s11:44px; --s12:48px;
  --s13:52px; --s14:56px;

  /* SECTION TIER. The scale above is a 4px COMPONENT scale and stops at 56px -
     nothing on it can space a 1360px-wide section, so every band on the site was
     padded with whichever of --s11..--s14 was reached for that day. Measured at
     1440 that produced four values doing one job (44, 48, 52, 56) and a
     content-to-content rhythm of 96-112px, which is tight against this measure
     and a 64px h2, and which collapsed unevenly on mobile - 72px between two
     sections while another held at 112.

     Each token is ONE SIDE. The rhythm between two adjacent sections is double
     it, because both contribute. --sec therefore reads as 96px on a phone and
     144px on a wide desktop.

     THE RULE: block padding on a section comes from these three and nowhere
     else, it goes on the <section> and never on its .wrap, and it is symmetric
     unless a comment states why not. Fluid so the rhythm stays proportional
     without a media query - the old per-breakpoint overrides are gone. */
  --sec-sm: clamp(40px, 4.4vw, 64px);   /* -> 80-128px rhythm.  A band that belongs
                                           to the section above it, not its own.  */
  --sec:    clamp(56px, 6.6vw, 96px);   /* -> 112-192px rhythm. The default.      */
  --sec-lg: clamp(72px, 8.4vw, 120px);  /* -> 144-240px rhythm. A section that has
                                           to be isolated to do its job.          */

  /* SECTION SURFACES. Two tones, and the rule is that ADJACENT SECTIONS MUST
     DIFFER - the tone change is the separator, so no section needs a border to
     be told apart from its neighbour.

     This replaced a 1px --line border-top on .hub, .tl-how and .tl-faq. Those
     existed because the alternation had already collided: .netsec and .hub were
     both --offwhite and sat next to each other, so a hairline was drawn to do
     the job the surface was supposed to do. A border between two sections 189px
     apart is a rule across the page, not a separator - nothing that far apart
     needs help being read as separate.

     PANELS INVERT. A panel on --surface takes --haze; a panel on --surface-alt
     takes --white. That is what keeps a card readable on either ground and is
     the reason a section can be flipped without redrawing its contents. */
  --surface:     var(--white);      /* #FFFFFF */
  --surface-alt: var(--offwhite);   /* #F9F9FB */

  /* MOTION. cubic-bezier(.2,.7,.3,1) was written out by hand 29 times across
     eight stylesheets before this token existed. It is the site's one easing
     curve and now has one name; a second curve needs a second token and a stated
     reason, not another literal. */
  --ease:      cubic-bezier(.2,.7,.3,1);
  --dur-line:  260ms;   /* the underline wipe - see .u-line */

  /* UNDERLINE. One rule weight and one offset for every animated underline on
     the site, so a link in the footer and a link in a paragraph are the same
     component at different sizes. */
  --u-h:   2px;
  --u-gap: 3px;

  /* Raised once already. The first tier ran 48/72 for --sec and still read tight
     against a 1360px measure - the failure of a 4px component scale is that it
     makes 48 feel like a large number when it is a small one at this width.
     TO RETUNE: change the clamp MAXIMA and leave the minima alone. The minima
     are the phone floor and are not the thing that ever felt cramped. */

  /* Radius scales with the surface it wraps: --r-lg for full cards, --r-md for
     compact rows. --r-md was left at 8px when the cards moved to 24 and it now
     reads as a different design language on the only element using it.
     16px is the concentric choice: a 69px row padded 12px wants its inner tile
     at outer-minus-padding, and --r-sm (4px) is already exactly that. */
  --r-sharp:0px; --r-sm:4px; --r-md:16px; --r-lg:24px; --r-pill:24px;

  /* Elevation */
  --e-subtle:0px 2px 4px rgba(0,0,0,.08);
  --e-card:0px 4px 12px rgba(0,0,0,.08);
  --e-high:0px 8px 24px rgba(0,0,0,.12);
  --e-max:0px 12px 32px rgba(0,0,0,.16);

  /* SYSTEM EXTENSION, declared rather than dropped in. The ladder above is
     built for CARDS - things about 320px across sitting in a grid - and every
     rung is ONE blur. At card size that is right and cheap. On a panel 880px
     wide it fails twice over: a 12px blur under an 880px box is a smudge along
     the bottom edge and nothing at the sides, so the panel has to fall back on
     a 1px --line to have an edge at all - and then the hairline is doing the
     work the elevation was supposed to do, which is why a big panel reads as a
     drawn rectangle rather than a raised sheet.

     --e-panel is four layers because a large surface needs each of them:
       1. a 1px ring, which REPLACES the border - the edge is now part of the
          elevation instead of a second system describing the same line
       2. a tight contact shadow that seats the panel on its ground
       3. a mid shadow, negative spread, that gives the sides an edge at all
       4. a wide ambient one, heavily pulled in, that carries the lift

     --ink-rgb, not black. Pure black over --surface-alt goes muddy grey; the
     site's own dark keeps the shade in the same family as the footer and the
     hero stage.

     WHERE IT APPLIES: full-width panels - things that hold a whole section
     rather than sit in a grid of siblings. Today that is .db-gate alone. A
     card in a column stays on --e-card; putting a 64px blur under a 200px box
     is the same mistake in the other direction.

     The choice is made PER COMPONENT, not per width. .db-gate keeps --e-panel
     down to 390px, where it is only 350px wide and most of the ambient layer
     goes unseen, because a panel that swaps elevation kind on resize reads as
     two different components to anyone who resizes. One component, one rung. */
  --e-panel:
    0 0 0 1px rgb(var(--ink-rgb) / .06),
    0 2px 4px rgb(var(--ink-rgb) / .04),
    0 12px 24px -8px rgb(var(--ink-rgb) / .08),
    0 32px 64px -24px rgb(var(--ink-rgb) / .14);

  /* Focus ring -----------------------------------------------------------
     ONE thickness for the whole site, declared here so it cannot drift a pixel
     per stylesheet. The ring was 3px at offset 2 and on a field that read as
     two edges - the control's own 1px border, a gap, then a 3px halo outside
     it - which is four pixels of purple around something that only asked to be
     focused.

     --focus-off is the gap a BUTTON, LINK or PILL keeps outside itself: those
     have no border of their own, so the ring has to sit off the shape to be
     seen. --focus-in is the same ring drawn INSIDE a FIELD's edge, painting
     over the 1px border it already has, so a focused input, textarea or select
     reads as one 2px border rather than as a border plus a halo.

     2px of --purple-deep is still 3:1 against both --white and --mist and
     clears the 2px perimeter WCAG 2.4.13 asks for. Do not go to 1px. */
  --focus-w:2px;
  --focus-c:var(--purple-deep);
  --focus-off:2px;
  --focus-in:-2px;

  /* Layering */
  --z-base:2; --z-dropdown:99; --z-header:100; --z-modal:2000;

  /* Width of the docked profile panel */
  --panel-w:520px;

  /* Type */
  --font:'Libre Franklin',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  --mono:ui-monospace,Consolas,'SF Mono',monospace;

  /* Per-member accent, overridden inline from the city ramp colour */
  --ac:var(--purple);
}


/* ==========================================================================
   02. Reset & base
   ========================================================================== */
*{box-sizing:border-box;margin:0}
html{scroll-behavior:smooth}

body{
  background:var(--white);
  color:var(--slate);
  font-family:var(--font);
  font-size:18px;
  font-weight:400;
  line-height:27px;
}

img{max-width:100%}
button{font:inherit;color:inherit}
/* Grid and flex children ignore the hidden attribute unless it is asserted. */
[hidden]{display:none!important}

:focus-visible{outline:var(--focus-w) solid var(--focus-c);outline-offset:var(--focus-off)}


/* ==========================================================================
   03. Typography
   ========================================================================== */
h1,h2,h3,h4{color:var(--black);letter-spacing:0;text-wrap:balance}

/* --- Tracking ------------------------------------------------------------
   THE RULE: tracking tightens as size grows, and the negative range starts at
   the h3 step. Nothing below 30px carries it.

   The scale had sizes and line-heights and no tracking at all, so every heading
   on the site was set at the 16px body's spacing. That is correct at 16px and
   wrong at 56, where the space between letters is drawn 3.5x larger than it was
   designed to be and the line reads as a row of separate words rather than a
   phrase. Type at display size needs LESS tracking, not the same amount.

   -.02 / -.015 / -.01 across display / h2 / h3. The steps are proportional to
   the size steps, so a heading tightens as it grows rather than one size being
   special.

   THE ONE EXCEPTION THIS PARAGRAPH USED TO RECORD IS GONE. It was .signup-h in
   brief-form.css, 23px at -.01em, predating the rule and left alone because it
   was a footer heading with no sibling at its size. The newsletter band was
   removed on 2026-08-28 and that heading went with it, so the scale above is
   now uniform with nothing sitting outside it. */
.t-display{font-size:clamp(48px,6.4vw,72px);font-weight:400;line-height:1.0;letter-spacing:-.02em}
/* 1.1, not 1.25. At 56px the old value drew 70px lines, so a two-line closer
   headline carried 14px of air between them and the two lines read as two
   statements. Display leading belongs near the type size; 1.25 is a BODY
   ratio, and it was the body's ratio applied to a heading three times the
   body's size. 61.6px at 56, still clear of the descenders. */
.t-h2{font-size:clamp(36px,4.4vw,56px);font-weight:500;line-height:1.1;letter-spacing:-.015em}
.t-h3{font-size:30px;font-weight:500;line-height:37.5px;letter-spacing:-.01em}
.t-h4{font-size:22px;font-weight:500;line-height:27.5px}

.t-body{font-size:18px;font-weight:400;line-height:27px}
.t-meta{font-size:12px;font-weight:400;line-height:18px}
.mono{font-family:var(--mono);font-size:13px;line-height:19.5px}

/* Section eyebrow, small uppercase label above a heading.
   Grey, not purple: at 12px the brand purple only reaches 4.42:1 on white,
   and the label should not compete with the purple emphasis in the heading
   directly beneath it. */
.eyebrow{
  font-size:12px;line-height:18px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;
  color:var(--gray);
}

.lede{font-size:18px;line-height:27px;color:var(--gray)}
.muted{color:var(--gray)}
/* Hyphenated proper nouns must not break across lines - "Asia-" / "Pacific"
   at the display size reads as a typo. */
.nb{white-space:nowrap}

/* --- Line breaking -------------------------------------------------------
   No line ends up carrying a single word. Two rules, because the two kinds of
   text want different things:

   balance, short display strings that are read as a shape (headings, taglines,
   blurbs, captions). Evens every line, so a two-line tagline never leaves
   "world." stranded underneath. Browsers cap it at a few lines, which is
   exactly the range these strings live in.

   pretty, running copy, where balancing whole paragraphs would be wrong.
   It only guards the end, pulling a word down rather than orphaning one.

   Both degrade to normal wrapping where unsupported: worse typography, never
   broken layout.

   Balance does the heavy lifting. Pretty is deliberately NOT applied site-wide:
   measured across five widths it orphans short strings that wrap cleanly on
   their own, so it is kept to the paragraph elements it helps.

   Three cases CSS cannot reach, fixed at source instead: a heading that missed
   its column by 7px (sized down at 479px), a blurb one word too long (reworded),
   and "X & y" footer links, where the only two-line split without a lone word is
   the one balance will not pick, so the ampersand is bound forward with &nbsp;
   in the markup. Verified 0 orphans at 1440/1024/768/390/320px. */
h1,h2,h3,h4,li,a,
.tagline,.lede,.blurb,.loc,.note,.pop-hint,.grid-empty,.stat dt,
.related-t b,.related-t span{
  text-wrap:balance;
}
p,dd,figcaption,blockquote{text-wrap:pretty}


/* ==========================================================================
   04. Layout utilities
   ========================================================================== */
.wrap{max-width:1400px;margin:0 auto;padding:0 var(--s6)}

/* The generic section utilities, on the tier like everything else. Nothing in
   the markup uses them today, which is exactly why they mattered: left at --s12
   and --s8 they were a trap for the next section that reached for a ready-made
   class and silently got the old, tight spacing back. */
.section{padding:var(--sec) 0}
.section-tight{padding:var(--sec-sm) 0}

.stack-sm > * + *{margin-top:var(--s3)}
.stack > * + *{margin-top:var(--s4)}


/* ==========================================================================
   05. Buttons & links
   ========================================================================== */
.btn{
  display:inline-block;position:relative;overflow:hidden;isolation:isolate;
  padding:12px 20px;
  font-family:var(--font);font-size:16px;font-weight:500;line-height:normal;
  border:0;border-radius:var(--r-pill);
  text-decoration:none;cursor:pointer;white-space:nowrap;
  transition:background-color 200ms ease-in-out,color 200ms ease-in-out,
             transform 180ms cubic-bezier(.2,.7,.3,1),
             box-shadow 220ms cubic-bezier(.2,.7,.3,1);
}

/* Directional fill. The darker colour wipes in from the left rather than the
   whole face switching at once, so the button reads as answering the cursor.
   West to east, the same direction as the map ramp and the footer rule.

   scaleX on a pseudo-element, not a width or background-position change: it is
   the one property here the compositor can animate without relayout or repaint. */
.btn-fill::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:var(--fill);
  transform:scaleX(0);transform-origin:left center;
  transition:transform 380ms cubic-bezier(.2,.7,.3,1);
}
.btn-fill:hover::before,
.btn-fill:focus-visible::before{transform:scaleX(1)}

/* A 1px lift and a tinted shadow. Small enough to register as weight shifting
   rather than the element jumping - and it never moves neighbouring content,
   since transform does not affect layout. */
.btn-fill:hover,
.btn-fill:focus-visible{transform:translateY(-1px);box-shadow:var(--lift)}

/* Press returns it to the surface and drops the shadow: down is the only
   direction a click should feel like. Faster than the lift, as contact is
   instant where the approach is gradual. */
.btn-fill:active{
  transform:translateY(0);box-shadow:none;
  transition-duration:80ms;
}

/* Primary, filled electric purple */
.btn-primary{
  background:var(--purple);color:var(--white);
  --fill:var(--purple-deep);
  --lift:0 6px 16px rgba(122,0,223,.34);
}
.btn-primary:hover{color:var(--white)}
.btn-primary:active{background:var(--plum)}

/* ==========================================================================
   NEUTRAL, and the rule it exists to enforce:

     PURPLE IS THE NETWORK. BLACK IS EVERYTHING ELSE.

   .btn-primary is reserved for the one action that starts a relationship with
   the network - brief the network, send brief, send my request, get in touch,
   start my daily brief. Everything else that has to look like a filled button
   takes this one: navigation, in-page utilities, external links, show-more,
   copy, build.

   WHY. Audited across six pages, purple was on four filled buttons on index
   alone, where "All field notes" and "Filter the directory" carried exactly the
   weight of "Brief the network". A signature colour spent on navigation stops
   being a signature, and the primary had no more pull than the utility beside
   it.

   .btn-secondary survives, but narrowly: it is the quiet COMPANION to a purple
   primary standing in the same row, where a black button would read as a second
   equal action rather than a subordinate one. Standalone utilities took neutral
   instead - a lavender button on its own was a primary in a soft voice.

   The face is --ink, the same dark the hero stage and the footer already use, so
   the button belongs to a colour the page has already met rather than
   introducing a second black.

   THE WIPE GOES TO PLUM, AND IT USED TO GO TO BLACK. Every filled button on
   this site answers the cursor with the same directional fill, and on this one
   alone you could not see it happen: #14161A to #000000 is a six-point shift,
   which is a wipe that technically runs and visibly does not. Measured against
   the primary beside it - #904FFF to #7A00DF - the mechanism was identical and
   the result was nothing. A hover that only a devtools panel can see is a
   hover that is not there.

   --plum is the right destination and not merely a visible one. It is a colour
   this site already uses as a dark ground, so the button is still travelling
   between two darks the page has met; it carries the brand's hue without being
   the brand's SIGNATURE, which keeps the rule above intact - electric purple
   still belongs to the one action that starts a relationship with the network,
   and a utility button wiping to plum is not competing for it.

   It lightens rather than darkens, which is the one place this button departs
   from the family. There is nowhere darker than --ink to go, and direction of
   travel matters less here than whether the travel is visible at all.

   The lift's shadow follows the wipe rather than the face, the way the primary's
   already does: rgba(122,0,223,.34) under purple, plum under this. */
.btn-neutral{
  background:var(--ink);color:var(--white);
  --fill:var(--plum);
  --lift:0 6px 16px rgba(58,35,100,.34);
}
.btn-neutral:hover,.btn-neutral:focus-visible{color:var(--white)}
/* Press holds where the wipe landed rather than jumping to a fourth value. */
.btn-neutral:active{background:var(--plum)}

/* Secondary, filled lavender. It takes the same wipe, lift and press as the
   primary - only the colours are quieter. The two sit side by side in the panel
   footer, and one answering the cursor while the other just changed colour read
   as the second one being broken.

   Filled, not keylined. Every other pill on this site is a fill - .tag, .peer,
   .tag-btn, .net-key - and each of those dropped its outline for the same
   reason: a hairline round a control draws the box before it draws the label.
   The secondary was the last outline left, which made it the odd one out rather
   than the quiet one.

   Lavender, not pale. The closers put this button on --mist (#EFEFF2), and
   #F4EDFF on #EFEFF2 is not a pill, it is a smudge. #D3B9FF separates from both
   white and mist, and plum on it is 7.6:1 - the label stays the strongest thing
   in the button. Saturated purple is left to the primary beside it.

   Padding is 12/20, the base .btn value: the old 10/18 existed only to pay for
   two pixels of border, so removing the border returns the box to standard and
   a primary and a secondary in a row keep the exact height they had. */
.btn-secondary{
  background:var(--lavender);color:var(--plum);border:0;padding:12px 20px;
  /* One step down the same hue rather than a new token: the wipe has to darken
     from #D3B9FF without becoming the primary's signature purple. */
  --fill:color-mix(in srgb,var(--lavender) 84%,var(--plum));
  --lift:0 6px 16px rgba(122,0,223,.18);   /* softer than primary's .34 */
}
/* No background here: the wipe supplies the darker ground, and setting it too
   would flood the whole face at once and cancel the direction.

   The ink holds at plum through all three states. It clears 4.5:1 on the rest
   fill and on the darker wipe alike, so nothing shifts under the cursor except
   the ground - which is the one thing the wipe is for. */
.btn-secondary:hover{color:var(--plum)}
.btn-secondary:active{background:color-mix(in srgb,var(--lavender) 84%,var(--plum));color:var(--plum)}

/* On dark plum surfaces */
.btn-on-dark{
  background:var(--white);color:var(--plum);
  --fill:var(--lavender);
  --lift:0 6px 18px rgba(0,0,0,.28);
}
.btn-on-dark:hover{color:var(--plum)}
/* The dark-surface secondary. Filled for the same reason .btn-secondary is:
   a translucent white fill separates from a plum or photographic ground on its
   own, and the keyline was only ever standing in for one. .16 is the lowest
   alpha that still reads as a pill over the hero's darkest frame; the wipe
   takes it to .28. Padding returns to the base 12/20 with the border gone, so
   the box does not change size. */
.btn-on-dark-ghost{
  background:rgba(255,255,255,.16);color:var(--white);border:0;padding:12px 20px;
  --fill:rgba(255,255,255,.28);
  --lift:0 6px 18px rgba(0,0,0,.28);
}
.btn-on-dark-ghost:hover{color:var(--white)}

/* ==========================================================================
   THE UNDERLINE, and it draws first letter to last.

   WHY IT WIPES AND WHY IT WIPES THAT WAY. The site already has a stated
   direction: .btn-fill's darker face scales in from the left, and the note there
   reads "West to east, the same direction as the map ramp and the footer rule."
   The underlines were the one interactive surface not following it - they simply
   appeared, all at once, which reads as a state being switched rather than an
   element answering the cursor.

   WHAT IT REPLACED. Three mechanisms doing one job, none of them animatable in a
   direction:
     .btn-ghost        box-shadow:inset 0 -2px 0    - fades in across its whole width
     .link             text-decoration:underline    - binary, cannot transition
     .footer-col a     text-decoration + offset     - same, plus its own offset value
   Three underline treatments meant three chances to drift and no way to make any
   of them move. This is one.

   A BACKGROUND GRADIENT, not a ::after. A pseudo-element needs a positioning
   context and gets clipped by any overflow:hidden ancestor; background-size works
   on a plain inline <a> inside a running paragraph, which is where most of these
   links actually live.

   IT ENTERS LEFT AND LEAVES RIGHT. background-position is not transitioned, so it
   snaps: on hover the rule is anchored left and grows to full width; on exit the
   anchor snaps right and the width collapses toward it. The line therefore sweeps
   THROUGH the word in one direction rather than rewinding back the way it came.
   ========================================================================== */
.u-line,
.link,
.btn-ghost,
.footer-col a,
.footer-legal a{
  text-decoration:none;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat;
  background-size:0% var(--u-h);
  background-position:100% 100%;
  transition:background-size var(--dur-line) var(--ease),
             color 180ms ease-in-out;
}
.u-line:hover,        .u-line:focus-visible,
.link:hover,          .link:focus-visible,
.btn-ghost:hover,     .btn-ghost:focus-visible,
.footer-col a:hover,  .footer-col a:focus-visible,
.footer-legal a:hover,.footer-legal a:focus-visible{
  background-size:100% var(--u-h);
  background-position:0 100%;
}

/* Ghost, text-only action. Keeps the button's padding so it still sits on a
   control's baseline in a row of buttons; the rule comes from .u-line above. */
.btn-ghost{background-color:transparent;color:var(--purple);border-radius:var(--r-sharp);padding:12px 0}
.btn-ghost:hover{color:var(--purple-deep)}

a{color:var(--purple)}
.link{color:var(--purple);font-weight:500}

/* A <button> that reads as a link, for the inline routes out of a sentence and
   for Sign out. HERE, and not in a page sheet, because three pages now use it -
   dashboard.html, account.html and login.html - and no one page sheet is loaded
   by all three. It lived in account.css, then in workspace.css, and each move
   happened because a page that needed it did not load the sheet it was in,
   which is the definition of a component that belongs in the shared one.
   .link and .btn-ghost above are written for anchors; this supplies the parts a
   <button> does not inherit. */
.ac-linkbtn{background:none;border:0;padding:0;font:inherit;cursor:pointer}
.btn-ghost.ac-linkbtn{padding:0}
.link:hover{color:var(--purple-deep)}
.link:active{color:var(--plum)}


/* ==========================================================================
   06. Cards, tiles & tags
   ========================================================================== */
/* Filled, not outlined - the same call .member-card made further down, in the
   same words: a stack of outlines on a white page draws a set of boxes before
   it draws the content in them. It matters more here than there, because these
   cards are white on white: the border was not describing the surface, it WAS
   the surface, and a card that needs a line to exist is a drawn rectangle
   rather than a ground. --haze is the token for this and has been all along. */
.card{
  background:var(--haze);
  border:0;
  border-radius:var(--r-lg);
  padding:var(--s6);
}
/* Tighter corner and padding in the 320px column: 24px of each reads as a
   consumer-app card rather than a reference panel. */
.profile-side .card{border-radius:16px;padding:var(--s5)}
.card-tinted{background:var(--pale);border:0;border-radius:var(--r-lg);padding:var(--s6)}

/* Logo tile, coloured initials with the real logo layered on top when it loads */
.tile{
  position:relative;overflow:hidden;flex:none;
  display:grid;place-items:center;
  background:var(--ac);color:var(--white);
  font-weight:600;letter-spacing:.02em;
  /* Percentage, not pixels, so the corner is a property of the shape rather than
     of each size class. Tiles are always square, so 25% is 25% of the side at
     every size - including the two places that resize a tile without touching
     its radius (the map popover, and the masthead tile under 480px), which is
     how sm ended up at 27% and md at 32% while the rest sat at 25%. */
  border-radius:25%;
}
/* A rounded square, never a circle: these are brand marks, not faces. Circles
   are already the panel's vocabulary for controls, and fitting a rectangular
   logo inside one costs 29% of its width - which the wider wordmarks cannot
   spare at 32px.

   Radius tracks size at ~25%. It used to be 14/14/12 across sm/md/lg, so the
   largest tile carried the sharpest corner - the ratio ran backwards against
   the tile it was on. */
.tile-xs{width:32px;height:32px;font-size:11px}
.tile-sm{width:48px;height:48px;font-size:15px}
.tile-md{width:56px;height:56px;font-size:18px}
.tile-lg{width:72px;height:72px;font-size:24px}
/* The one documented exception to the ratio: nested inside a 16px row with 12px
   of padding, the concentric corner is outer-minus-padding, not 25%. */
.related-item .tile{border-radius:var(--r-sm)}
/* The mark sits on white so brand colours stay true. No ring around it - the
   logo should read as the mark itself, not as a framed thumbnail.
   The inset is a percentage, not 4px: a flat 4px ate an eighth of a 32px tile
   and barely showed on a 72px one, so small marks lost the room they needed
   most. */
.logoimg{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;background:var(--white);padding:8%;
}
/* The tile keeps its accent colour only while it is showing initials. Left in
   place behind a logo it fringes the rounded corners, which reads as a border. */
.tile:has(.logoimg){background:var(--white)}

/* A mark published only in a light version needs the ground it was drawn for -
   the member's own brand colour, carried in on --markbg, falling back to plum
   when none is on file. The tile has to be recoloured too, or the white would
   fringe the corners exactly as the accent colour does above. */
.logoimg-onbg{background:var(--markbg,var(--plum))}
.tile:has(.logoimg-onbg){background:var(--markbg,var(--plum))}

/* --- Selected work -------------------------------------------------------
   Optional per member. The aspect-ratio is fixed so the grid holds its shape
   before anything decodes - without it every image landing shifts the sections
   below it. */
.work-grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:var(--s4);margin-top:var(--s5);
}
.work-item{
  margin:0;overflow:hidden;border-radius:var(--r-md);
  background:var(--offwhite);border:1px solid var(--line);
}
.work-item img{
  display:block;width:100%;height:100%;
  aspect-ratio:4/3;object-fit:cover;
  transition:transform 320ms cubic-bezier(.2,.7,.3,1);
}
.work-item:hover img{transform:scale(1.03)}
.work-credit{margin-top:var(--s3);color:var(--ash);font-size:13px;line-height:20px}

/* The cap. Positional, not a class: work-lightbox.js rebuilds each figure as a
   <button> and writes className wholesale, so a class on the tile is erased the
   moment the viewer script runs. nth-child survives it. The control is a sibling
   of the grid, so it is not counted. */
.work-grid:not(.is-open) > *:nth-child(n+7){display:none}
.work-more{
  margin-top:var(--s4);padding:11px 19px;
  font:inherit;font-size:14px;line-height:20px;
  color:var(--black);background:var(--mist);
  border:0;border-radius:var(--r-pill);
  cursor:pointer;transition:background 160ms ease,color 160ms ease;
}
/* Filled, not outlined - the same call every other pill on the site makes. The
   1px is kept in the padding (11/19, not 10/18) so the control holds the height
   it had, and the hover moves the ground instead of the edge. */
.work-more:hover{background:var(--pale);color:var(--plum)}

/* --- Inside: the room, under the work ------------------------------------
   Subordinate by construction. Same tiles, shallower crop, smaller minimum
   track, so on any width it prints more-across-and-flatter than the grid above
   it. That difference is the hierarchy: read in one glance the eye takes the
   work as the subject and the office as the footnote it is.

   When a member has no work imagery this block never renders - their pictures
   are promoted into .work-grid above and get the full 4:3 treatment. Nobody
   loses a section, the section only changes rank. */
.place-sub{margin-top:var(--s10);padding-top:var(--s6);border-top:1px solid var(--line)}
.place-head{
  margin:0 0 var(--s4);
  color:var(--ash);font-size:13px;line-height:20px;font-weight:600;
  letter-spacing:.06em;text-transform:uppercase;
}
/* Flex at a fixed tile width, NOT the grid's auto-fit/1fr. auto-fit stretches
   the tracks to fill the row, so a member with two office photos got two tiles
   at half the container each - 250px tall against the work grid's 264, which is
   not a footnote, it is a second gallery. A fixed 200px tile holds the same
   size whether there are two of them or five, and lands at roughly half the
   area of a work tile at every width. wrap rather than scroll: this is three
   pictures, not a carousel. */
.place-strip{display:flex;flex-wrap:wrap;gap:var(--s3)}
.place-strip .work-item{flex:0 0 auto;width:200px}
.place-strip .work-item img{aspect-ratio:16/10;height:auto}
/* One column below the point the profile itself stacks, where 200px of a
   ~340px column reads as a mistake rather than a choice. */
@media (max-width:560px){
  .place-strip .work-item{width:calc(50% - var(--s3) / 2)}
}

/* Service tags */
.tags{display:flex;flex-wrap:wrap;gap:var(--s2)}
/* The overflow count. Lighter than a real service, because it is a pointer to
   the profile rather than a label in its own right. */
.tag-more{color:var(--ash);cursor:default}
/* Neutral at rest. Every card carries three of these, so tinting them purple
   put the accent everywhere and left nothing for the things that actually use
   it. Purple is kept for the interactive states below. */
.tag{
  background:var(--mist);color:var(--gray);
  font-size:14px;line-height:normal;font-weight:400;
  border-radius:var(--r-pill);padding:7px 15px;
}
.tag-sm{font-size:12px;padding:4px 12px}

/* Tags that actually do something. Inside the panel every service filters the
   directory, so it earns a hover state and a pointer. */
/* These are <button>, while the card's tags are <span>, so they arrive carrying
   the UA's outset border and button chrome. Stripped here so an interactive tag
   is pixel-identical to a static one at rest, the difference should be the
   hover state, not a stray outline. */
/* These filter the directory, unlike the identical-looking chips on the cards.
   An outline used to be what said so - white fill, hairline border, against the
   flat chips beside it.

   It does not any more. A panel section holds six to twelve of these, and six
   to twelve hairlines is a grid of boxes: the borders became the loudest thing
   in the body and every one of them said the same nothing. They keep the
   neutral fill the static chips have and earn their difference on hover, which
   is the moment the distinction is actually useful. The border is kept at 1px
   transparent so the box does not move by a pixel between states. */
.tag-btn{
  appearance:none;cursor:pointer;
  background:var(--mist);border:1px solid transparent;
  transition:background-color 200ms ease-in-out,border-color 200ms ease-in-out,color 200ms ease-in-out;
}
.tag-btn:hover{background:var(--pale);color:var(--plum)}
.tag-btn:active{background:var(--purple);color:var(--white)}

/* Key/value list */
.kv{display:grid;gap:var(--s3)}
.kv-row{display:flex;gap:var(--s4);align-items:baseline;font-size:16px;line-height:24px}
.kv-k{color:var(--gray);flex:none;width:108px;font-size:14px}
.kv-v{color:var(--black);font-weight:500}


/* ==========================================================================
   07. Site header, white bar, black mark, single action
   ========================================================================== */
.site-header{
  background:var(--white);
}
.site-header .wrap{
  min-height:72px;
  display:flex;align-items:center;gap:var(--s5);
}

.brand{display:inline-flex;align-items:center;min-height:44px}
/* Two assets rather than a CSS filter: a stale stylesheet would otherwise leave
   the white mark sitting invisibly on the white bar. */
.brand img{display:block;height:32px;width:auto}

/* The header carries exactly one action, pushed to the far edge. */
.site-header .btn{margin-left:auto}

/* --- The section nav ----------------------------------------------------

   Members / Insights / Free tools / Newsletter, on the six pages that are a
   destination somebody browses to: insights, the reader, tools, daily-brief,
   newsletter and profile.

   It lived in insights.css until profile.html needed it, and profile does not
   load that sheet - it is a member page, not an insights page, and pulling the
   whole insights bundle in for one flex row would have brought the note-card
   and filter-bar rules with it. The name is historical: it is the site's
   section nav now, not the insights one, and it is left alone because
   docs/brief.md and the header comment on every one of those six pages names
   it.

   index.html CARRIES IT TOO, as of the change that put it there, and shares the
   slot with the contextual "Selected market" breadcrumb rather than losing it to
   the breadcrumb: at rest the nav holds the slot, and when a market is selected
   the crumb takes it - .is-focus has faded the rail and the hero copy out by
   then, so the crumb is genuinely the navigation at that moment. The swap rule
   lives in hero-map.css beside .hdr-context, which is the state that drives it.
   Nothing here needs to know about it.

   Still NOT on the signed-in pages: dashboard/account carry .dash-rail. */
.ins-nav{display:flex;gap:var(--s6);margin-left:var(--s8);margin-right:auto}
.ins-nav a{
  color:var(--gray);font-size:15px;font-weight:500;text-decoration:none;
  padding:6px 0;
  transition:color 180ms ease-in-out,box-shadow 180ms ease-in-out;
}
.ins-nav a:hover{color:var(--plum)}
/* The current page is marked, not linked away from. A nav of two where neither
   says where you are is a nav of two decorations. */
.ins-nav a[aria-current="page"]{color:var(--plum);box-shadow:inset 0 -2px 0 var(--purple)}

/* Below this the bar is the wordmark and the one action. The section nav is a
   desktop convenience; the footer carries the same four links on every page and
   is what a phone actually uses. */
@media (max-width:720px){
  .ins-nav{display:none}
}

/* The member switcher that used to live here is gone: the sidebar now lists all
   eleven members outright, and a dropdown of the same eleven beside it was the
   same journey offered twice. What is left is the stepper's own wrapper. */
.membernav{display:grid;gap:var(--s3)}


/* ==========================================================================
   08. Site footer
   ========================================================================== */
/* Deep plum closes the page. The header is white and near-empty, so the weight
   belongs at the bottom - and it gives the nav that left the header a home. */
.site-footer{background:var(--plum);color:var(--lavender);position:relative}

/* Longitude ramp, west to east, same gradient the map markers are coloured from */
.footer-sun{height:4px;background:var(--ramp)}

.site-footer .footer-top{
  display:flex;flex-wrap:wrap;gap:var(--s12) var(--s10);
  padding-top:var(--s12);padding-bottom:var(--s12);
}

.footer-brand{flex:1 1 300px;max-width:360px}
.site-footer .brand{min-height:0}
.site-footer .brand img{height:30px}
.footer-blurb{
  color:var(--lavender);font-size:15px;line-height:24px;
  margin:var(--s5) 0 var(--s6);
}

.footer-cols{
  flex:2 1 480px;
  display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s6);
}
.footer-h{
  color:var(--white);
  font-size:12px;line-height:18px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;
  margin-bottom:var(--s4);
}
.footer-col ul{list-style:none;margin:0;padding:0}
.footer-col li + li{margin-top:var(--s1)}
.footer-col a{
  color:var(--lavender);
  font-size:16px;line-height:24px;
  display:inline-flex;align-items:center;min-height:32px;
  /* Colour only. The background-size transition that draws the rule is declared
     once in the .u-line group and must not be reset here - a bare
     `transition:color` at this specificity silently removed the wipe from every
     footer link while leaving the underline itself working, which is the kind of
     drift that looks like nothing is wrong. */
  transition:color 200ms ease-in-out,
             background-size var(--dur-line) var(--ease);
}
.footer-col a:hover{color:var(--white)}

.site-footer .footer-base{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--s3) var(--s6);
  padding-top:var(--s5);padding-bottom:var(--s6);
  border-top:1px solid rgba(255,255,255,.15);
}
.footer-note{color:var(--lavender);font-size:13px;line-height:20px;opacity:.85}
.footer-legal{
  list-style:none;margin:0 0 0 auto;padding:0;
  display:flex;gap:var(--s5);
}
.footer-legal a{
  color:var(--lavender);font-size:13px;text-decoration:none;
  display:inline-flex;align-items:center;min-height:32px;
}
.footer-legal a:hover{color:var(--white)}


/* ==========================================================================
   09. Masthead, deep plum surface shared by the profile hero and the panel
   ========================================================================== */
.masthead{position:relative;overflow:hidden;background:var(--plum);color:var(--white)}
/* Flat plum, no wash. AMIN's own site runs on solid surfaces, so the two radial
   gradients that used to sit here have gone rather than being toned down. */
.masthead > *{position:relative}
.masthead::after{content:"";position:absolute;left:0;right:0;bottom:0;height:3px;background:var(--ramp)}

.masthead h1,.masthead h2{color:var(--white)}
.masthead .crumb{
  font-size:12px;line-height:18px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--lavender);
}
/* Underlined on hover only: three permanently underlined links above the agency
   name would read as louder than the name. The 44px target is grown with padding
   rather than height, so the crumb keeps its own line box. */
.masthead .crumb a{
  position:relative;
  color:inherit;text-decoration:none;
  padding:12px 0;
  transition:color 200ms ease-in-out;
}

/* The rule is drawn on, not switched on. scaleX from a zero-width line is the
   one way to do this the compositor can run without laying the text out again.

   It enters from the left and leaves to the right rather than retracting the way
   it came: every directional move on this site runs west to east - the button
   fill, the footer rule, the longitude ramp the whole network is ordered by - and
   a rule that rewound would be the only thing travelling backwards. Swapping the
   origin between the two states is what buys the one-way trip. */
.masthead .crumb a::after{
  /* right is inset by the tracking, not zeroed: .14em of letter-spacing is added
     after the last character too, so a full-width rule finishes past the E it is
     supposed to stop under. */
  content:"";position:absolute;left:0;right:.14em;bottom:10px;height:1px;
  background:currentColor;
  transform:scaleX(0);transform-origin:right center;
  transition:transform 340ms cubic-bezier(.2,.7,.3,1);
}
.masthead .crumb a:hover,
.masthead .crumb a:focus-visible{color:var(--white)}
.masthead .crumb a:hover::after,
.masthead .crumb a:focus-visible::after{
  transform:scaleX(1);transform-origin:left center;
}
.masthead .crumb [aria-current]{color:var(--white)}
.masthead .tagline{color:var(--lavender);margin-top:var(--s4);max-width:46ch;font-size:18px;line-height:27px}
.masthead .geo{display:flex;flex-wrap:wrap;gap:var(--s3) var(--s6);margin-top:var(--s6);font-size:14px;color:var(--lavender)}
.masthead .geo b{color:var(--white);font-weight:500}
.masthead .actions{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s7)}


/* ==========================================================================
   10. Stat bar
   ========================================================================== */
/* No keyline: the pale fill already separates the strip from the white above
   and below, so the hairlines only added weight. */
.statbar{background:var(--pale)}
/* The profile page already opens on a plum masthead and closes on a plum footer.
   A third purple band 120px below the first left roughly half the page sitting on
   a tinted surface. White with hairlines reads as a data strip, which is what it
   is. */
.statbar-plain{background:var(--white);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
.statbar-plain .stat dd{color:var(--slate)}
.statbar{padding:var(--sec-sm) 0}
.statbar .wrap{padding-top:0;padding-bottom:0}

/* Fixed columns rather than auto-fit: at 1400px auto-fit left the figures
   clustered against the left edge with half the strip empty. */
.stats{display:grid;grid-template-columns:repeat(4,1fr);margin:0}
.stats-4{grid-template-columns:repeat(4,1fr)}
/* Its values are words ("English, Hindi", "UTC +5:30"), not two-digit numerals,
   so they are set at heading scale rather than figure scale. */
.stats-4 .stat dd{font-size:clamp(20px,1.9vw,26px);line-height:1.2;letter-spacing:0}

/* Label above figure. Reading order in the markup is label-then-number so the
   dl stays semantic; `column-reverse` puts the number on top visually.

   Content is centred in its track and the padding is symmetric. Left-aligning
   inside equal tracks made the strip read as lopsided: every figure sat hard
   against its own divider while the trailing space after it varied with the
   label's width, and the last column left a wide gap at the right edge. */
.stat{
  display:flex;flex-direction:column-reverse;justify-content:flex-end;
  align-items:center;text-align:center;
  padding:0 var(--s5);
  border-left:1px solid var(--line);
}
.stat:first-child{border-left:0}

.stat dd{
  margin:0;color:var(--plum);
  font-size:clamp(34px,3.2vw,44px);font-weight:500;line-height:1.05;
  text-wrap:balance;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;
}
.stat dt{
  margin-top:var(--s2);
  color:var(--gray);font-size:12px;line-height:18px;
  letter-spacing:.1em;text-transform:uppercase;
}

/* --- Count-up entrance --- */
/* Only the figures lift, and only once .is-counted lands. Without JS the class
   never arrives, so nothing is ever left mid-transition or invisible. */
.statbar.is-counted .stat dd{animation:statrise 620ms cubic-bezier(.2,.7,.3,1) backwards}
.statbar.is-counted .stat:nth-child(1) dd{animation-delay:0ms}
.statbar.is-counted .stat:nth-child(2) dd{animation-delay:90ms}
.statbar.is-counted .stat:nth-child(3) dd{animation-delay:180ms}
.statbar.is-counted .stat:nth-child(4) dd{animation-delay:270ms}
.statbar.is-counted .stat:nth-child(5) dd{animation-delay:360ms}
@keyframes statrise{
  from{opacity:0;transform:translateY(10px)}
  to{opacity:1;transform:none}
}

/* ==========================================================================
   11. Page: map + directory
   ========================================================================== */

/* --- Hero --- */
.hero{padding:var(--s12) 0 var(--s9);text-align:center}
.hero .eyebrow{margin-bottom:var(--s4)}
.hero h1{max-width:none}                     /* one line where the viewport allows */
.hero h1 em{font-style:normal;color:var(--purple)}
.hero .lede{margin:var(--s5) auto 0;max-width:60ch;font-size:20px;line-height:30px}

/* --- Map --- */
.mapsec{padding:0 0 var(--s8)}
.globe-wrap{position:relative;max-width:980px;margin:0 auto}
.loaderr{padding:var(--s10) var(--s5);text-align:center;color:var(--gray);font-size:16px}
.loaderr small{display:block;margin-top:var(--s2);color:var(--ash);font-size:14px}
.globe{
  display:block;width:100%;height:auto;
  touch-action:none;cursor:grab;
  -webkit-user-select:none;user-select:none;
}
.globe.is-dragging{cursor:grabbing}
.globe.is-dragging .tag-text,
.globe.is-dragging .tag-agency{opacity:0}      /* labels would collide mid-spin */

.globe-limb{fill:none;stroke:var(--globe-limb);stroke-width:1}
.graticule{fill:none;stroke:var(--globe-grat);stroke-width:1;stroke-linecap:round}

/* Land in two tones, the region against the rest of the world, each split into
   three depth bands so the dots fade toward the limb and the sphere reads round.

   The field is blue and the markers stay brand purple. Painting the terrain in
   the same hue as the pins made the two compete; separating them leaves purple
   carrying a single meaning here. */
.oceandots{fill:none;stroke:var(--globe-ocean);stroke-width:2.2;stroke-linecap:round}
.oceandots.band-1{stroke-opacity:.5}

.landdots,.landdots-apac{fill:none;stroke-width:2.8;stroke-linecap:round}
.landdots{stroke:var(--globe-land)}
.landdots-apac{stroke:var(--globe-apac)}
.band-1{stroke-opacity:.72}
.band-2{stroke-opacity:.44}

.badge-dot{fill:var(--plum)}

.marker{cursor:pointer;outline:none}
.marker .core{transition:fill 180ms ease-out,r 180ms cubic-bezier(.2,.7,.3,1)}
/* The halo is a wash behind the dot, not a second dot. It scales up from just
   inside the core as it fades in, so the hover grows out of the marker rather
   than appearing around it, opacity alone popped it into place at full size. */
.marker .halo{
  opacity:0;transform:scale(.5);
  transform-origin:center;transform-box:fill-box;
  transition:opacity 240ms cubic-bezier(.2,.7,.3,1),
             transform 240ms cubic-bezier(.2,.7,.3,1);
}

/* --- Selected city ---------------------------------------------------------
   Size and a white keyline do the work, with every other marker dimmed behind
   it. That ordering matters: the shift here stays inside one hue family, so the
   state has to survive on form alone - which also means it survives for anyone
   who cannot separate the two greens. */
.marker.is-open .core{
  fill:var(--pin-active);r:7.5;
  stroke:var(--white);stroke-width:2.5;paint-order:stroke fill;
}
.marker.is-open .halo{fill:var(--pin-active)}
.marker.is-open .pulse{stroke:var(--pin-active)}

/* The rest step back while one is open. The label only half-dims, it still has
   to be readable, since these are the markers you would switch to next. */
.globe.has-open .marker:not(.is-open) .core{opacity:.45}
.globe.has-open .marker:not(.is-open) .pulse{animation:none;opacity:0}
.globe.has-open .marker:not(.is-open) .tag-text{opacity:.7}
/* A wash, not a fill. This used to resolve to opacity:1, which overrode the
   element's own 0.2 and drew a solid disc, the reason hover read as heavy. */
.marker:hover .halo,
.marker:focus-visible .halo,
.marker.is-linked .halo{opacity:.22;transform:scale(1)}
.marker.is-open .halo{opacity:.3;transform:scale(1)}

/* A little growth on the dot itself. Most of the feel comes from here, the halo
   reads as atmosphere, the core reads as the thing responding. */
.marker:hover .core,
.marker:focus-visible .core{r:6.4}
.marker:focus-visible .core{stroke:var(--plum);stroke-width:2}
.marker .pulse{transform-origin:center;transform-box:fill-box;animation:markerPulse 2.8s ease-out infinite}
@keyframes markerPulse{
  0%{transform:scale(.45);opacity:.5}
  70%,100%{transform:scale(1.9);opacity:0}
}
.marker .tag-text,
.marker .tag-agency{
  font-family:var(--font);font-size:13px;font-weight:500;
  fill:var(--plum);pointer-events:none;
  paint-order:stroke fill;stroke:var(--white);stroke-width:3px;stroke-linejoin:round;
  transition:fill 160ms ease-out,opacity 160ms ease-out;
}

/* City at rest, agencies on hover — the two cross-fade in place. The map stays
   legible as a map, and the "who is here" layer costs a gesture rather than
   permanent density. */
.marker .tag-agency{opacity:0}
.marker:hover .tag-text,
.marker:focus-visible .tag-text,
.marker.is-open .tag-text{opacity:0}
.marker:hover .tag-agency,
.marker:focus-visible .tag-agency,
.marker.is-open .tag-agency{opacity:1}
/* Hover takes the pin's own green, not purple, purple is the selected state,
   and a brighter purple on hover would outrank the thing it is not. */
/* Hover reads green; selected reads plum. Same specificity, so selected is
   declared last — a marker you have opened keeps its state while the pointer
   is still resting on it. */
.marker:hover .tag-agency,.marker:focus-visible .tag-agency{fill:var(--pin-hover-ink)}
.marker.is-open .tag-agency{fill:var(--pin-active-ink);font-weight:600}
.marker .badge-text{font-family:var(--font);font-size:10px;font-weight:600;fill:var(--white);pointer-events:none}


/* --- Directory --- */
/* index.html surface run: hero (dark) - directory - netsec - hub - closer -
   footer (dark). Alternating from here down, so no two neighbours share a tone
   and none of them needs a border. */
.directory{padding:var(--sec) 0;background:var(--surface)}
.directory-head{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--s4);margin-bottom:var(--s7)}
.directory-head .count{align-self:center;color:var(--gray);font-size:14px}

/* Sort control. Three readings of the same eleven agencies, so it reads as a
   segmented choice rather than three separate buttons - and it sits on the
   neutral chip fill, leaving purple to mark which one is active. */
.sortbar{
  display:inline-flex;align-self:center;margin-left:auto;
  background:var(--mist);border-radius:var(--r-pill);padding:3px;
}
.sort-btn{
  border:0;background:none;cursor:pointer;
  border-radius:var(--r-pill);padding:6px 14px;
  font-family:var(--font);font-size:14px;line-height:normal;color:var(--gray);
  transition:background-color 200ms ease-in-out,color 200ms ease-in-out;
}
.sort-btn:hover{color:var(--plum)}
.sort-btn.is-on{background:var(--white);color:var(--plum);font-weight:500;box-shadow:var(--e-subtle)}

/* The live service filter, set from a panel tag. It reads as a removable state
   rather than a control, because clearing it is the only thing it does. */
.filter-chip{
  display:inline-flex;align-items:center;gap:var(--s2);align-self:center;
  background:var(--lavender);border:0;border-radius:var(--r-pill);
  padding:7px 9px 7px 15px;color:var(--plum);font-size:14px;cursor:pointer;
  transition:background-color 200ms ease-in-out;
}
.filter-chip b{font-weight:500}
.filter-chip svg{
  width:20px;height:20px;padding:4px;flex:none;border-radius:50%;
  fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;
  background:rgba(58,35,100,.08);
}
/* The chip was pale-on-white with a lavender keyline and went lavender on
   hover, which spent the whole state change on an edge. Filled at rest instead,
   and the hover steps the ground one notch down the same hue - the ladder
   .btn-secondary uses. The 1px is paid back in the padding so the chip keeps
   the height it had beside the sort control. */
.filter-chip:hover{background:color-mix(in srgb,var(--lavender) 84%,var(--plum))}
/* ==========================================================================
   Closing band. Two audiences, two actions: the client brief leads, the
   membership ask sits beside it in a quieter card so it reads as an aside
   rather than as a competing offer.
   ========================================================================== */
/* No rule above the section: the box has its own edge, and two horizontal
   lines a few pixels apart read as a seam. */
.closer{border-top:0}
/* The directory section already ends with 48px of its own padding, so a matching
   48px here would have stacked to 96 above the box and left 48 below it. The top
   is zero and the directory's padding is the single gap, which makes the space
   above and below the box equal. */
/* Symmetric, on the section, from the section tier - and that retires a whole
   chain. The top padding used to be zeroed here and handed back by whichever
   adjacent-sibling rule matched whatever preceded the closer on that page:
   `.netsec + .closer`, `.hub + .closer`, `.tl-faq + .closer`, each with its own
   value and its own breakpoint override. Four rules to answer one question. The
   closer now carries its own spacing and does not care what it follows.
   --sec-lg rather than --sec: it is the ask, and isolation is what earns it. */
.closer{padding:var(--sec-lg) 0;background:var(--surface-alt)}
.closer .wrap{padding-top:0;padding-bottom:0}
/* A contained panel rather than more open page. Flat on white with only a
   hairline above it, the close read as another section of the directory; the
   box gives the one conversion moment on the page an edge of its own.
   Neutral, not pale purple: the metrics band is the page's single branded
   surface and a second tinted block would halve its weight. */
.closer .wrap{text-align:center}
/* NO SURFACE OF ITS OWN. This was --mist, then briefly --white, and it should
   have been neither: the fill existed because .closer had no background and the
   box had to supply the whole tone change by itself. Once the section became the
   tinted half of the alternation, the box was a container inside a container -
   two nested surfaces carrying one message, with a rounded edge drawn around the
   page's only ask.

   The element stays because it is the semantic wrapper the markup and the script
   both reference. It just stops painting, and stops padding: the section's own
   --sec-lg is the spacing, and --s12 on top of it was the section tier being
   quietly doubled by a child. */
/* Paints nothing and spaces nothing. Kept only because the markup and the brief
   script both address it; if either stops, delete the element rather than leave
   a 1352px wrapper that looks like it must be doing something. */
.closer-box{background:transparent;border:0;padding:0}
/* THE RULE: NO H2 MAY MATCH OR EXCEED ITS PAGE'S H1.

   This heading was briefly promoted to .t-display to give the page's one ask
   more weight than the section headings above it. The reasoning was sound and
   the execution was not, because it was applied without checking each page's
   own H1. Measured after that change:

     index.html      H1 60px   closer h2 72px   <- h2 LARGER than the h1
     insights.html   H1 72px   closer h2 72px   <- identical
     tools.html      H1 72px   closer h2 72px   <- identical

   A section heading cannot outrank the page title, and the fix is not a
   per-page size. The closer IS a section, so it takes section-heading size.

   WHAT MAKES IT THE CLOSE IS NOT SIZE. It is --sec-lg isolation, the tinted
   surface, and being the only section on the page that asks for something. The
   type carries its share through VOICE instead: weight 400 against .t-h2's 500
   and the display's tighter -.02em tracking, so it reads as a statement rather
   than a label at exactly the same 56px. Same size, different register, and the
   document hierarchy stays true.

   The 16ch cap is expressed in ch so the character count protects the line
   break: the measure moves with the type instead of the line loosening. */
.closer h2{
  max-width:16ch;margin-inline:auto;
  font-weight:400;letter-spacing:-.02em;
}
/* 18/27, which is .t-body, NOT the 17/26 this used to carry. 17px and a 26px
   line appear nowhere else on this site - not in the scale, not in a component,
   not on another page - so the deck under the single most important headline on
   three pages was the one piece of copy set to a size the system does not have.
   It is also the deck that sits closest to a 56px headline, which is where the
   step down was most visible. */
.closer-p{margin:var(--s5) auto 0;color:var(--gray);font-size:18px;line-height:27px;max-width:48ch}

/* THE TERMINAL ACTION IS NOT THE SAME SIZE AS A NAV ACTION. Every button on the
   site was .btn's 16px at 12/20 padding, so the header's "Brief the network"
   and the button the whole page is built to earn were pixel-identical at 43px
   tall. That is not restraint, it is a missing step: the closer's button had no
   more weight than the one a visitor scrolls past in the first second.

   15/28 padding and 18px puts it at 52px against the header's 43. Both numbers
   are on scale - 18px is .t-body, and the padding pair keeps the same 1.87
   horizontal-to-vertical ratio 12/20 has, so the pill's proportions are the
   ones the small one already uses rather than a new shape.

   NOT A NEW .btn-lg CLASS, deliberately. A size that has to be remembered and
   applied is a size that gets forgotten on the fourth closer; hanging it off
   .closer means the three conversion moments on this site are the same size by
   construction, which is the same argument brief.md already records for the
   closer box itself.

   The margin goes 28 -> 32 with it: a larger button needs the gap above it to
   grow or it reads as having drifted up into the deck. */
.closer .btn{
  margin-top:var(--s8);
  padding:15px 28px;
  font-size:18px;
}

/* THE LIFT SCALES WITH THE CONTROL, and it did not. .btn-fill's hover is a 1px
   rise and a 6/16 shadow, drawn against the 43px pill every other button on this
   site is. The closer's is 52px, and the same values under a face 21% larger
   read as LESS: the rise is a smaller fraction of the height, and a 16px blur
   spreads under a wider element to something flatter. The terminal action was
   answering the cursor more quietly than the nav button it is deliberately
   bigger than, which inverts the whole argument above.

   The rise doubles and the shadow follows it - 12px down, 28px of blur - with
   the alpha coming DOWN from .34 to .28 so the larger shadow is not also a
   heavier one. It is the same shadow further away, which is what a taller lift
   looks like.

   PER VARIANT, BECAUSE --lift CARRIES ITS OWN COLOUR. Each closer button keeps
   the hue its variant already had; only the geometry changes. Three lines rather
   than one is the cost of --lift holding a colour and a shape in one token, and
   it is not worth splitting that token for three selectors.

   Hung off .closer for the reason the size is: the three conversion moments on
   this site stay identical by construction rather than by memory. The wipe, the
   press and the focus ring are untouched - this changes how far the button
   rises, not how it behaves. */
.closer .btn-fill:hover,
.closer .btn-fill:focus-visible{transform:translateY(-2px)}
.closer .btn-primary {--lift:0 12px 28px rgba(122,0,223,.28)}
.closer .btn-neutral {--lift:0 12px 28px rgba(58,35,100,.28)}
.closer .btn-on-dark {--lift:0 12px 30px rgba(0,0,0,.24)}
/* Press still returns it flat, and from 2px it has further to travel - which is
   the point. .btn-fill:active already zeroes the transform and drops the shadow
   in 80ms; nothing here overrides it. */

/* THE CLOSER'S FOOTNOTE. Two closers grew one on the same day - tools.html's,
   and index.html's once its form moved to contact.html - and a second copy of
   four declarations is how a system gets two of everything. It lives here
   because .closer lives here.

   It is .av-note / .ta-note's own values: 14px, --ash, a --line hairline with
   --s5 under it, <b> going to --gray at 600 for the one phrase worth anchoring.
   Those two sit in left-aligned cards on the tool pages; this is the centred
   version of the same element, and the only difference is the measure.

   62ch AND NOT THE 70 THEY USE. ch is the advance of "0", about .69em in Libre
   Franklin, so .closer-p's 48ch at 18px is ~594px and the same width at 14px is
   48 x 18/14 = 62ch. The deck and the note share one left and one right edge
   rather than nearly sharing them - which daily-brief.css already records
   getting wrong once, on a note set wider than the paragraph above it.

   THE GAP IS --s8, THE SAME AS THE BUTTON'S OWN. Below that the note reads as a
   caption hanging off the pill rather than as the section's last line. */
.closer-note{
  margin:var(--s8) auto 0;padding-top:var(--s5);
  border-top:1px solid var(--line);
  max-width:62ch;
  color:var(--ash);font-size:14px;line-height:22px;
}
.closer-note b{color:var(--gray);font-weight:600}

.grid-empty{margin-top:var(--s6);color:var(--gray);font-size:16px}

.member-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:var(--s5)}

/* Eleven members never fill the last row, so the twelfth tile carries the one
   thing a directory of members should say to a non-member. A tinted,
   borderless fill rather than an outlined card, so it reads as an
   invitation and not as a twelfth agency. */
.join-card{
  order:999;                       /* always the end of the set */
  display:flex;flex-direction:column;gap:var(--s4);
  background:var(--mist);border:0;border-radius:var(--r-lg);
  padding:var(--s6);text-decoration:none;
  transition:background-color 200ms ease-in-out,transform 200ms ease-in-out;
}
.join-card:hover{background:#E6E6EB;transform:translateY(-2px)}
.join-mark{width:44px;height:auto;margin-bottom:var(--s1)}
.join-t{color:var(--black);font-size:20px;font-weight:500;line-height:26px}
.join-p{color:var(--gray);font-size:16px;line-height:24px}
.join-go{margin-top:auto;padding-top:var(--s4);color:var(--purple);font-size:16px;font-weight:500}
.join-card:hover .join-go{color:var(--purple-deep)}

.member-card{
  position:relative;
  display:flex;flex-direction:column;
  /* Filled, not outlined. Eleven outlines across a white page drew a grid of
     boxes before it drew a set of members; the tint separates each card from
     the page without a line doing the work. */
  background:var(--haze);border:0;border-radius:var(--r-lg);
  padding:var(--s6);text-align:left;cursor:pointer;
  transition:background-color 200ms ease-in-out,box-shadow 200ms ease-in-out,transform 200ms ease-in-out;
}
/* Hover lifts the card rather than outlining it - a purple border here competed
   with the border the active card uses to show which panel is open. The global
   :focus-visible ring still covers keyboard focus. */
.member-card:hover,.member-card:focus-visible{
  background:var(--mist);box-shadow:var(--e-high);transform:translateY(-2px);
}
/* The neutral chip fill sits on the same grey family as the card now, so the
   tags need white to stay separate from the surface under them. */
.member-card .tag{background:var(--white)}
/* The mark sits on its own line. Beside the name it took ~55px off the headline,
   and the grid's narrowest track is 280px - too little for the longest member
   names, which then wrapped while the rest stayed on one line. Given the full
   card width the longest name clears it by 19px even in the narrowest column. */
/* Spacing groups the card rather than dividing it evenly: one flat 12px gap
   between every element gave the name, the location and the description equal
   weight, so nothing read as belonging to anything else. The name and its city
   are now one unit, with the larger steps falling between the groups. */
.member-card-top{display:flex;margin-bottom:var(--s4)}
.member-card-id{display:block}
/* The logo tile and its gap take ~55px off the headline's line, and at the
   narrowest column (323px, four across) the longest member names wrapped while
   the rest stayed on one line, leaving the card tops ragged. 19px against a
   trimmed tile leaves ~17px of slack on the longest name - enough that a font
   still loading, or a fallback face, cannot tip it onto a second line. */
/* 19px, not 20: the longest member name in the set, "Span Media
   Communications", measures 274px against a 273px column at 20px, so it broke
   to two lines and stranded "Communications". One step down clears it at 260px
   with room to spare, and no other name is close to the limit. */
.member-card h3{font-size:19px;font-weight:500;line-height:26px;text-wrap:balance}
.member-card .loc{display:block;margin-top:2px;color:var(--gray);font-size:14px;line-height:21px}
.member-card .blurb{margin-top:var(--s4);color:var(--gray);font-size:16px;line-height:24px;min-height:48px}
.member-card .tags{margin-top:var(--s5);margin-bottom:var(--s4)}
.member-card-foot{
  display:flex;align-items:center;margin-top:auto;
  padding-top:var(--s4);border-top:1px solid var(--line);
}
.member-card-foot b{color:var(--purple);font-size:16px;font-weight:500}
.member-card:hover .member-card-foot b{color:var(--purple-deep)}
/* The raw offset is infrastructure data, not a selling point, and eleven of
   them at once is noise. It appears only when the directory is sorted by time
   zone, where it is the reason the order changed. */
.member-card-foot .utc{display:none;margin-left:auto;color:var(--ash);font-size:12px}
.member-grid.show-utc .member-card-foot .utc{display:block}


/* ==========================================================================
   12. Page: member profile
   ========================================================================== */
/* --sec-sm, like .statbar: both are strips that read as part of what is above
   them rather than as bands of their own. */
.profile-mast{padding:var(--sec-sm) 0}
.profile-mast .wrap{padding-top:0;padding-bottom:0}
/* The mark and the name are one lockup, not a badge parked above a headline.
   Stacked, the tile read as a stray element and cost the hero ~90px of height.
   Wrapping is allowed rather than forced onto one line: below about 900px
   "Span Media Communications" needs two, and the tile centres against the whole
   block instead of dangling at the first cap line. */
.profile-mast .mast-id{display:flex;align-items:center;gap:var(--s5);margin-top:var(--s6)}
.profile-mast .mast-id h1{min-width:0}

/* The grid keeps the page's full 1400 so the About heading starts on the same
   line as the agency name above it - narrowing and centring this block broke
   that edge, which was worse than the problem it fixed.
   Instead the text column is capped at its own comfortable width and the sidebar
   is pinned to the right edge, which puts the slack between the two columns as a
   deliberate gutter rather than at the end of every line. */
.profile-cols{
  display:grid;grid-template-columns:minmax(0,1fr) 320px;
  justify-content:space-between;gap:var(--s14);
  /* Horizontal padding restated, not zeroed: `padding:48px 0` here overrode
     .wrap's own 24px side padding, so this whole column has been sitting 24px
     left of the masthead above it. */
  align-items:start;padding:var(--sec) var(--s6);
}
/* No width cap on the reading column. It had one at 880px, which was 104px
   narrower than the track it sits in - and that unused 104px sat right next to
   the 48px grid gap, so the space between the text and the sidebar read as
   152px while the space between the text and the gutter rail read as 48px. One
   of the three columns was three times further from its neighbour than the
   other, which is what made the middle of the page look broken rather than
   airy.

   Nothing needed the cap: paragraphs carry their own measure at 66ch below,
   which is what actually keeps prose readable, and the headings, work grid and
   chip rows are all better off using the full column. */
.profile-block{margin-bottom:var(--s14)}
.profile-block:last-child{margin-bottom:0}
/* Six headings at one size is six headings with no hierarchy. The page gets a
   single lead - the agency's own story - and the rest step down under it. */
.profile-block h2{margin:0 0 var(--s4);font-size:26px;font-weight:500;line-height:34px}
.profile-lead h2{font-size:34px;line-height:42px;margin-bottom:var(--s5)}
.profile-block p{max-width:66ch}
.profile-block p + p{margin-top:var(--s4)}
.profile-block .tags{margin-top:var(--s4)}

.profile-side{position:sticky;top:91px;display:grid;gap:var(--s4)}
/* These were 12px uppercase purple - the same treatment the links use, on text
   that is not clickable. Purple now means "you can act on this" and nothing
   else, so the card headings drop to plain sentence-case ink. */
.profile-side .card h3{
  font-size:14px;line-height:21px;font-weight:600;
  letter-spacing:0;text-transform:none;color:var(--black);
}
.profile-side .kv{margin-top:var(--s4)}

.disclaimer{color:var(--ash);font-size:12px;line-height:18px}



/* --- At a glance: variable column count ---
   Six of the eleven members publish neither a founding year nor a headcount, so
   the strip is built from whatever the agency actually states and sized to fit.
   An em dash set at 44px read as a broken page; a missing column does not. */
.stats-2{grid-template-columns:repeat(2,1fr)}
.stats-3{grid-template-columns:repeat(3,1fr)}
/* These values are words and clock times ("English, Hindi", "14:32"), not
   two-digit numerals, so they are set at heading scale, not figure scale. */
.stats-words .stat dd{font-size:clamp(20px,1.9vw,26px);line-height:1.2;letter-spacing:0}

/* --- Masthead: the live local clock ---
   The network's claim is a working day that never stops, so the hero states the
   time it is there right now. Tabular figures, or the minute digit shifts the
   whole line every thirty seconds. */
.profile-mast .geo .geo-time{display:inline-flex;align-items:baseline;gap:6px}
.profile-mast .geo .geo-time b{font-variant-numeric:tabular-nums;letter-spacing:.02em}

/* --- Services, grouped by discipline ---
   The tags are each agency's own words. The discipline heading above them is the
   network's, which is what makes two members comparable. */
.disc{margin-top:var(--s5)}
.disc + .disc{margin-top:var(--s6)}
.disc-h{
  margin:0 0 var(--s3);
  color:var(--black);font-size:15px;line-height:23px;font-weight:600;
  letter-spacing:0;text-transform:none;
}

/* --- Selected clients ---
   Names rather than logos: the marks are not ours to serve, and a wall of
   mismatched logotypes at ten different weights reads worse than a clean list. */
.clients{display:flex;flex-wrap:wrap;gap:var(--s2);margin-top:var(--s5)}
/* No keyline: these sit next to the service tags, which carry none, and an
   outlined pill beside a filled one reads as two different components. The
   client chip stays distinct on scale alone - larger type, taller pill. */
.client{
  display:inline-flex;align-items:center;gap:var(--s2);min-height:36px;padding:6px 14px;
  border-radius:var(--r-pill);
  background:var(--mist);color:var(--slate);
  font-size:15px;line-height:22px;
}
/* A chip carrying a mark gets a little more room on the left, so the logo sits
   inside the pill rather than against its curve. */
.client-has-mark{padding-left:10px}
/* Height, never width: these are wordmarks as often as symbols, so Hyundai is
   nearly eight times the width of NBA at the same cap height. Fixing the height
   is what makes a row of them sit on one line; fixing the width would set them
   at eight different sizes.

   Grey at rest. Twenty-eight brand palettes next to a page built on one purple
   is the fastest way to make a client list look like a banner ad - so the marks
   are drained to ink and weight, and only the one under the cursor is allowed
   its own colour back. */
.client-mark{
  height:18px;width:auto;max-width:96px;object-fit:contain;flex:none;
  filter:grayscale(1);opacity:.72;
  transition:filter 200ms ease,opacity 200ms ease;
}
.client:hover .client-mark{filter:none;opacity:1}
.source-note{margin-top:var(--s4);color:var(--ash);font-size:12px;line-height:18px}

/* --- Sidebar: brief this agency ---
   The page used to end at an outbound link, which spent the visitor's intent off
   the network. This card is the one that carries the member's own accent, so the
   action reads as theirs rather than as site chrome. */
.card-cta{box-shadow:var(--e-subtle)}
.card-cta p{margin-top:var(--s3);color:var(--gray);font-size:15px;line-height:23px}
.card-cta .btn{display:block;margin-top:var(--s5);text-align:center;white-space:normal}
/* The pair is one group: the second button sits close to the first, and the
   group sits well clear of the sentence above it. 20 then 12, not 20 and 20 -
   even gaps left the second button belonging to neither. */
.card-cta .btn + .btn{margin-top:var(--s3)}

/* --- Sidebar: recognition --- */
.creds{list-style:none;margin:var(--s4) 0 0;padding:0;display:grid;gap:var(--s3)}
.creds li{position:relative;padding-left:var(--s5);color:var(--slate);font-size:15px;line-height:22px}
.creds li::before{
  content:"";position:absolute;left:0;top:8px;
  width:6px;height:6px;border-radius:50%;background:var(--ac);
}

/* --- Sidebar: the whole directory ---
   Eleven agencies is few enough to show in full and too many to hide behind a
   dropdown, which is what this replaces. Ordered west to east, so the rail tells
   the same longitude story as the map, the tile ramp and the footer sun: reading
   down the list is reading forward through the working day.

   Height-capped rather than left to run. Not because a tall rail breaks the
   sticky: it does not, the browser scrolls a sticky element taller than its
   frame until the bottom catches, and everything below stays reachable. It is
   that all eleven rows add ~250px to a rail that is already 1,400px, pushing
   Fast facts and the stepper a further screen down for a list nobody needs to
   read end to end. Six and a half rows, and the half is the point: a list cut
   mid-row is visibly a list with more in it, which a list cut cleanly between
   rows is not. */
/* The fades that mark "more above" and "more below" have to sit outside the
   scroller, so the list gets a positioned wrapper. Negative margin plus matching
   padding inside bleeds both to the card edge without widening the card, which
   is what lets a row highlight run the full width of its surface. */
.dirscroll{position:relative;margin:var(--s4) calc(var(--s5) * -1) 0}
.dirscroll::before,.dirscroll::after{
  content:"";position:absolute;left:0;right:0;height:36px;z-index:1;
  /* z-index, because the scroller is positioned too: without it the list paints
     over the fades and they are invisible at every scroll position. */
  pointer-events:none;opacity:0;transition:opacity 180ms ease-in-out;
}
.dirscroll::before{top:0;background:linear-gradient(to bottom,#fff 20%,rgba(255,255,255,0))}
.dirscroll::after{bottom:0;background:linear-gradient(to top,#fff 20%,rgba(255,255,255,0))}
/* Set by the scroll handler, so a fade only ever appears over content there is
   more of. A permanent one dims the first and last agency for no reason. */
.dirscroll[data-up]::before{opacity:1}
.dirscroll[data-down]::after{opacity:1}

.dirlist{
  position:relative;                    /* the offsetParent the scroll-into-view maths uses */
  max-height:366px;overflow-y:auto;overscroll-behavior:contain;
  padding-left:var(--s3);padding-right:var(--s3);
  /* The native bar is hidden in favour of the drawn one below. The panel's
     approach - thin it and tint it - cannot work in a 320px card: Chrome's
     `thin` is a fixed ~11px that reserves layout width, and setting
     scrollbar-color makes it ignore the ::-webkit- width you asked for. At two
     thirds of a seven-row track that paints a solid grey slab, the heaviest ink
     in a card made of light rows, sitting on top of the fades. */
  scrollbar-width:none;                 /* Firefox, and Chrome 121+ */
  -ms-overflow-style:none;
}
.dirlist::-webkit-scrollbar{width:0;height:0}

/* The drawn scrollbar that used to ride this list - an absolutely positioned
   thumb in the card's gutter, draggable - is gone. No markup has carried
   .dirscroll or .dirbar for some time, so the rules styled nothing. The list
   hides the native bar and scrolls plainly. */

/* Two columns, mark and label. The row's own inset is 8px and the list's is 12px,
   which puts the tile 20px from the card edge, level with the heading above it,
   while leaving the highlight room to sit around the text rather than against
   it. */
.dirrow{
  display:grid;grid-template-columns:32px minmax(0,1fr);align-items:center;
  gap:0 var(--s3);
  padding:var(--s2);
  border-radius:10px;text-decoration:none;
  transition:background-color 160ms ease-in-out;
}
.dirrow + .dirrow{margin-top:2px}
/* Name and city are two rows, so the mark and the marker have to span both or
   they centre against the name alone and sit a few pixels high. */
.dirrow .tile{grid-column:1;grid-row:1 / span 2}
.dirrow b{
  grid-column:2;display:block;color:var(--black);
  font-size:14px;font-weight:500;line-height:20px;
  /* Two of the eleven names are long enough to wrap to three lines in 200px,
     which would make their rows twice the height of the rest. */
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.dirrow span{
  grid-column:2;display:block;color:var(--gray);
  font-size:12px;line-height:18px;
}
a.dirrow:hover{background:var(--pale)}
a.dirrow:hover b{color:var(--plum)}

/* The current member is not a link, so it is not styled as one. Two marks carry
   that, not three: the row is tinted, and the city line trades for the word that
   says why. The accent bar that used to sit in a reserved left column is gone -
   at 3px it was a third signal for a state the tint and the label had already
   made unambiguous, and it was the only thing in the card holding every name
   15px off the edge. */
.dirrow-here{background:var(--offwhite);cursor:default}
.dirrow-here b{color:var(--black)}
.dirrow-here span{color:var(--ac);font-weight:500}

/* Count sits with the heading rather than under it: the card's job is answered
   in one line, "these are all of them, and there are eleven". */
.dir-h{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s3)}
.dir-h .dir-n{
  color:var(--ash);font-size:12px;font-weight:400;line-height:18px;
  font-variant-numeric:tabular-nums;
}

/* --- Discipline definitions ---
   The tags say what the agency calls its services; this says what the network
   means by the category. Set at reading size, not caption size: it is content. */
.disc-d{margin:0 0 var(--s4);color:var(--gray);font-size:16px;line-height:25px;max-width:64ch}
.disc + .disc{margin-top:var(--s8)}

/* --- Peers in the same discipline ---
   A profile that only points at its three nearest neighbours reads as a leaf
   page. These are the members who answer the same kind of brief. */
.peers{margin-top:var(--s5);display:grid;gap:var(--s6)}
.peer-list{display:flex;flex-wrap:wrap;gap:var(--s2)}
.peer{
  display:inline-flex;align-items:baseline;gap:var(--s2);
  padding:9px 15px;border:0;border-radius:var(--r-pill);
  background:var(--mist);text-decoration:none;
  transition:background-color 200ms ease-in-out;
}
.peer b{color:var(--black);font-size:15px;font-weight:500;line-height:22px}
.peer span{color:var(--gray);font-size:13px}
.peer:hover{background:var(--pale)}
.peer:hover b{color:var(--plum)}

/* --- Their day and yours ---
   Three figures and a sentence. The figures are set at the stat-strip's word
   scale rather than its numeral scale, since two of the three are phrases. */

.noscript-note{padding:var(--s10) 0}


/* ==========================================================================
   13. Slide-in profile panel
   ========================================================================== */
/* Narrow screens: the panel covers the page over a scrim.
   Wide screens (see the docked block below): the page shrinks beside it. */
.scrim{
  position:fixed;inset:0;z-index:calc(var(--z-modal) - 1);
  background:rgba(58,35,100,.5);
  opacity:0;pointer-events:none;transition:opacity 220ms ease-in-out;
}
.scrim.is-open{opacity:1;pointer-events:auto}

/* The panel is a column: a scrolling body between a sticky bar and a fixed
   action footer, so the primary action never scrolls out of reach. */
.panel{
  position:fixed;top:0;right:0;bottom:0;z-index:var(--z-modal);
  width:min(560px,100vw);background:var(--white);
  border-left:1px solid var(--line);
  box-shadow:var(--e-max);
  display:flex;flex-direction:column;
  transform:translateX(102%);
  transition:transform 280ms cubic-bezier(.32,.72,.28,1);
}
.panel.is-open{transform:translateX(0)}
/* The native Windows scrollbar is ~17px wide with stepper arrows at both ends —
   a piece of OS chrome heavier than anything else in the panel, and sitting
   right against the masthead. Thinned and tinted so it reads as part of the
   surface rather than bolted to the edge of it. */
.panel-scroll{
  flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  scrollbar-width:thin;
  scrollbar-color:var(--scroll-thumb) transparent;
}
/* For Safari before 18.2, which has no scrollbar-color. Chrome and Firefox use
   the two standard properties above and ignore these. */
.panel-scroll::-webkit-scrollbar{width:10px}
.panel-scroll::-webkit-scrollbar-track{background:transparent}
.panel-scroll::-webkit-scrollbar-thumb{
  background:var(--scroll-thumb);border-radius:var(--r-pill);
  /* Transparent border plus padding-box clip insets the thumb, so it floats in
     the track instead of filling it edge to edge. */
  border:3px solid transparent;background-clip:padding-box;
}
.panel-scroll::-webkit-scrollbar-thumb:hover{
  background:var(--purple-deep);background-clip:padding-box;
}

/* --- Docked mode: the panel takes its own column and the page reflows --- */
@media (min-width:1200px){
  body{transition:padding-right 280ms cubic-bezier(.32,.72,.28,1)}
  body.has-panel{padding-right:var(--panel-w)}

  .scrim{display:none}                    /* nothing to dim, the page stays live */
  .panel{width:var(--panel-w);box-shadow:none}

  /* Show which member the open panel belongs to. The tint alone carries it -
     an outline on top made the active card read as a selected form field. */
  .member-card.is-active{
    background:var(--pale);
    box-shadow:var(--e-card);
  }
  .member-card.is-active .member-card-foot b{color:var(--purple-deep)}
  /* The neutral chip fill turns muddy over the lavender tint - grey on lilac
     reads as dirty rather than quiet. White separates cleanly instead. */
  .member-card.is-active .tag{background:var(--white)}
  .member-card.is-active .tag-more{color:var(--gray)}
}

/* Top padding is the slot the sticky bar is pulled into. */
/* Bottom padding matches the sides, so the geo strip sits inside an even inset
   instead of being pinned against the accent bar. The 56px top is the reserve
   the sticky bar is pulled back over. */
.panel-mast{padding:56px var(--s7) var(--s7);margin-top:-56px}

/* The bar holds every panel-level control, step back, step forward, dismiss,    and sticks so they stay reachable while the body scrolls under it. */
/* --condense is scrubbed 0->1 from the scroll position rather than flipped at a
   threshold, so the bar fills in under your finger instead of snapping once you
   cross a line. Nothing here transitions: the scroll position IS the timeline,
   and a duration on top of it would lag behind the content. */
.panel-bar{
  position:sticky;top:0;z-index:3;
  display:flex;align-items:center;gap:var(--s4);min-height:56px;
  padding:var(--s2) var(--s7);
  /* The fill runs 3x ahead of the scrub so the bar is opaque by a third of the
     way through. It has to occlude early: the masthead's own logo and name pass
     directly under it, and a half-transparent bar shows them straight through
     the label. */
  background:rgb(var(--plum-rgb) / calc(var(--condense,0) * 3));
  box-shadow:0 1px 0 rgb(255 255 255 / calc(var(--condense,0) * .14));
}
/* White at reduced opacity rather than lavender: it sat above a per-city wash
   before, and on flat plum it still reads better than lavender against the
   heading beside it. The crumb is the one taken out of flow: the identity needs
   the real flex slot so its name can ellipsize against the nav rather than run
   under it. */
.panel-bar .crumb{
  position:absolute;left:var(--s7);top:50%;transform:translateY(-50%);
  color:rgba(255,255,255,.78);white-space:nowrap;
  /* Reserve = the nav's own 136px plus the gap. Stale reserves let a long crumb
     slide under the controls before it ellipsizes. */
  max-width:calc(100% - 160px);overflow:hidden;text-overflow:ellipsis;
  /* The crumb and the identity share this slot, so they hand over in sequence
     rather than crossfading through each other: the crumb is gone by 0.4, which
     is exactly where the name starts. Overlapping them dissolved two labels
     into each other mid-scroll. */
  opacity:calc(1 - var(--condense,0) * 2.5);
}
.panel-nav{display:flex;align-items:center;gap:var(--s2);flex:none;margin-left:auto}

/* Only the states that must be binary stay on a class, a half-faded label
   should not be hoverable or reachable, and there is no half-way for that. */
.panel.is-condensed .panel-bar .crumb{pointer-events:none}

.bar-id{
  display:flex;align-items:center;gap:var(--s3);min-width:0;flex:1;
  /* Starts exactly where the crumb finishes (0.4), so the slot is never shared
     and never empty. */
  pointer-events:none;opacity:calc(var(--condense,0) * 2.5 - 1);
}
.bar-id b{
  color:var(--white);font-size:16px;font-weight:500;line-height:24px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* No fill at rest. On the plum masthead a white glyph already has all the
   contrast it needs, and three filled chips beside the logo and name made the
   controls the loudest thing in the header. The chip is the hover state now,
   which is the moment it is actually useful. */
.panel-icon{
  position:relative;
  width:36px;height:36px;flex:none;display:grid;place-items:center;
  background:none;border:0;border-radius:50%;
  color:rgba(255,255,255,.8);cursor:pointer;
  transition:background-color 200ms ease-in-out,color 200ms ease-in-out,
             transform 140ms cubic-bezier(.2,.7,.3,1);
}
.panel-icon svg{
  width:16px;height:16px;
  fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;
}
/* The drawn size stays 36px so the bar keeps its proportions, but the target is
   grown to 44 through a transparent pad. Vertical only inside the stepper: the
   buttons are neighbours there, and overlapping targets would steal each
   other's taps. */
.panel-icon::after{content:"";position:absolute;inset:-4px 0}
.panel-close::after{inset:-4px}
.panel-icon:hover{background:rgba(255,255,255,.18);color:var(--white)}
.panel-icon:active{transform:scale(.92)}
.panel-icon:focus-visible{outline:var(--focus-w) solid var(--white);outline-offset:var(--focus-off)}

/* Grouping by proximity rather than by a container: the pair sits tight, and
   dismiss sits a clear step away. Spacing carries the same meaning a track did,
   without adding a shape to the header. */
.panel-step{display:flex;align-items:center;gap:var(--s1);flex:none}
.panel-close{margin-left:var(--s4)}

/* Logo and name read as one identity rather than two stacked blocks, which also
   buys back the vertical space the panel needs for its content. */
.panel-id{display:flex;align-items:center;gap:var(--s4);margin-top:var(--s3)}
.panel-id h2{min-width:0}
.panel-mast .tile{box-shadow:0 0 0 1px rgba(255,255,255,.2)}  /* purple initials on plum need an edge */
.panel-mast .tile:has(.logoimg){box-shadow:none}
.panel-mast h2{font-size:28px;font-weight:500;line-height:34px;letter-spacing:-.01em}
.panel-mast .tagline{
  margin-top:var(--s3);font-size:16px;line-height:24px;max-width:42ch;
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;
}

/* Place, local wall clock and offset are three separate facts, so they get a rule
   above them and hairlines between rather than running together in one line. The
   clock is the point: it answers "can I call them now" for a network that spans
   seven zones. */
.panel-mast .geo{
  display:flex;flex-wrap:wrap;align-items:center;
  /* Uneven on purpose: the rule introduces the strip, so it sits closer to what
     follows it than to the tagline it has just closed off. Equal 16/16 left it
     floating between the two and belonging to neither. */
  margin-top:var(--s6);padding-top:var(--s4);
  border-top:1px solid rgba(255,255,255,.16);
  font-size:12px;line-height:18px;gap:10px var(--s6);
  color:rgba(211,185,255,.85);
}
/* No gap on the place, or the flex box opens a space before its comma. */
.panel-mast .geo > span{display:inline-flex;align-items:center}
.panel-mast .geo .geo-time{gap:6px}
.panel-mast .geo > span + span::before{
  content:"";width:1px;height:12px;margin-right:var(--s6);
  background:rgba(255,255,255,.22);
}
.panel-mast .geo b{color:var(--white);font-weight:500}
.panel-mast .geo .geo-place{font-size:13px}
.panel-mast .geo .geo-time b{font-size:14px;letter-spacing:.02em}

.panel-body{padding:var(--s7)}

/* Rhythm: a label sits close to the thing it labels, and sections are pushed
   well apart. These two gaps used to be 16px and 28px - near enough that a
   label read as floating between two blocks rather than belonging to the one
   under it. 12 against 32 groups them unambiguously. */
.panel-body section,
.panel-body .lead{margin-bottom:var(--s8)}
.panel-body .tags,
.panel-body .kv,
.panel-body .related{margin-top:var(--s3)}

/* Scoped off .eyebrow. As `.panel-body section p` this outranked .eyebrow and
   set every section label to 16px body size - uppercase, wide-tracked and the
   same size as the content it introduces, so nothing led anything. */
.panel-body section > p:not(.eyebrow){color:var(--gray);font-size:16px;line-height:24px;margin-top:var(--s3)}
.panel-body section > p + p{margin-top:var(--s3)}

/* A pill that filters looks exactly like a pill that doesn't, so the label says
   so - but quietly. The hint modifies the label, so it has to sit below it:
   same size, lighter colour, no tracking, sentence case. */
.eyebrow-hint{
  margin-left:var(--s2);color:var(--ash);
  font-size:12px;font-weight:400;letter-spacing:0;text-transform:none;
}

/* The opening paragraphs carry the agency's own voice, so they are set larger
   and darker than the supporting sections rather than as body filler. */
.panel-body .lead p{color:var(--slate);font-size:17px;line-height:27px}
.panel-body .lead p + p{margin-top:var(--s4)}

/* Facts are reference, not headline: hairlines instead of a filled card, so the
   loudest surface in the body is no longer the least differentiating content. */
.panel-body .kv{gap:0}
.panel-body .kv-row{
  display:grid;grid-template-columns:auto 1fr;gap:var(--s5);align-items:baseline;
  padding:var(--s3) 0;
}
/* Rules separate facts from each other; they do not box the group in. Enclosing
   rules turned a single-fact member - which is most of them - into a lone strip
   floating under its label. */
.panel-body .kv-row + .kv-row{border-top:1px solid var(--line)}
.panel-body .kv-row:first-child{padding-top:0}
.panel-body .kv-row:last-child{padding-bottom:0}
.panel-body .kv dt{color:var(--gray);font-size:13px;line-height:20px;white-space:nowrap}
.panel-body .kv dd{
  margin:0;color:var(--black);font-size:16px;line-height:24px;font-weight:500;text-align:right;
}

/* Somewhere to go next that isn't back to the grid to start again. */
.related{display:grid;gap:var(--s2)}
/* Filled rather than outlined, for the same reason as the pills above: a
   discipline's roster is six of these stacked, and six hairlines read as a form
   rather than as a list of members. The fill is the same neutral, so a row and
   a chip are the same surface at two sizes. */
.related-item{
  display:flex;align-items:center;gap:var(--s3);width:100%;
  background:var(--mist);border:1px solid transparent;border-radius:var(--r-md);
  padding:var(--s3);text-align:left;cursor:pointer;
  transition:background-color 200ms ease-in-out,border-color 200ms ease-in-out,
             box-shadow 200ms ease-in-out,transform 200ms ease-in-out;
}
/* Hover lifts, it does not tint. The pale fill is spoken for: it marks the
   member whose panel is open, and the grid stays visible beside the docked
   panel, so a tinted hover here would put two meanings on one colour. The lift
   is smaller than the member card's because the row is smaller - same gesture,
   scaled to the surface. */
/* Coming off the mist, the row goes white and picks up the card shadow - it
   lifts off the surface toward the reader rather than tinting, which is the
   same rule as before and now has somewhere to lift from. Tinting is still
   spoken for: pale marks the member whose panel is open, and the grid stays
   visible beside the docked panel, so a tinted hover here would put two
   meanings on one colour. */
.related-item:hover{
  background:var(--white);box-shadow:var(--e-card);transform:translateY(-1px);
}
/* The row's job is "go here", so the chevron leads on hover the way the member
   card's "View profile" link shifts colour. */
.related-go{transition:transform 200ms ease-in-out}
.related-item:hover .related-go{transform:translateX(3px)}
.related-t{display:grid;min-width:0;flex:1}
.related-t b{color:var(--black);font-size:16px;font-weight:500;line-height:24px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.related-t span{color:var(--gray);font-size:13px;line-height:19px}
.related-go{
  width:16px;height:16px;flex:none;color:var(--purple);
  fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round;
}

.panel-body .disclaimer{border-top:1px solid var(--line);padding-top:var(--s4)}

/* Fixed footer, outside the scroll container. */
.panel-actions{
  flex:none;display:flex;flex-wrap:wrap;gap:var(--s3);
  padding:var(--s4) var(--s7);
  background:var(--white);border-top:1px solid var(--line);
  box-shadow:0 -8px 20px rgba(0,0,0,.05);
}
.panel-actions .btn{flex:1;text-align:center;white-space:nowrap}

/* Content settles in behind the panel's own slide rather than arriving with it. */
.panel.is-open .panel-body > *{animation:panelRise 320ms cubic-bezier(.32,.72,.28,1) backwards}
.panel.is-open .panel-body > *:nth-child(2){animation-delay:40ms}
.panel.is-open .panel-body > *:nth-child(3){animation-delay:80ms}
.panel.is-open .panel-body > *:nth-child(4){animation-delay:120ms}
.panel.is-open .panel-body > *:nth-child(n+5){animation-delay:160ms}
@keyframes panelRise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* Stepping between members crossfades instead of teleporting one identity into another. */
.panel-body{transition:opacity 120ms ease-in-out}
.panel-body.is-swapping{opacity:0}


/* ==========================================================================
   14. Responsive
   ========================================================================== */

/* --- Tablet and below: collapse the header and the multi-column layouts --- */
@media (max-width:1023px){
  /* Brand, back link and the primary action share one row on both pages. */
  .site-header .wrap{
    flex-wrap:wrap;gap:var(--s3);
    min-height:64px;padding-top:var(--s3);padding-bottom:var(--s3);
  }
  .brand img{height:28px}

  .profile-cols{grid-template-columns:1fr;gap:var(--s10)}
  .profile-side{position:static}

  /* A directory sitting in a card: nothing is sticky here, so the cap that
     protected the sticky rail is just a scroller inside a page that already
     scrolls. The list runs in full, and at full width it sets in columns
     rather than one 11-row ribbon.

     Scoped to .card deliberately. The profile's directory now hangs off the
     sticky bar as a panel, where the cap is the only thing keeping it inside
     the window - un-capping it there would run the eleven off the bottom of
     the screen. */
  .card .dirlist{
    max-height:none;overflow:visible;
    display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));
    gap:2px var(--s5);
  }
  .card .dirrow + .dirrow{margin-top:0}
  .member-grid{grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}

  /* Four across gets too tight to read, and four items split evenly two-by-two
     where three columns would strand a single figure on its own row. */
  .stats{grid-template-columns:repeat(2,1fr);gap:var(--s6) 0}
  .stat:nth-child(2n+1){border-left:0}

  /* Brand block takes its own full-width row above the three link columns */
  .site-footer .footer-top{gap:var(--s10);padding-top:var(--s10);padding-bottom:var(--s10)}
  .footer-brand{flex:1 1 100%;max-width:440px}
  .footer-cols{flex:1 1 100%}
}

/* --- Mobile --- */
@media (max-width:767px){
  body{font-size:16px;line-height:24px}

  .wrap{padding:0 var(--s5)}
  .section{padding:var(--s9) 0}
  .hero{padding:var(--s9) 0 var(--s7)}
  .hero .lede{font-size:18px;line-height:27px}
  /* The lockup is a desktop and tablet arrangement. At 390px the mark would
     take 72 of the 350 usable pixels and "Communications" no longer fits the
     remainder at any size worth setting, so mark and name stack again.
     The display size steps down with it: at the shared 48px minimum the longest
     name overflowed the masthead and was silently clipped by its overflow:hidden
     - a bug that pre-dates the lockup and is fixed here. */
  .profile-mast .mast-id{display:block}
  .profile-mast .mast-id .tile{width:56px;height:56px;font-size:18px}
  .profile-mast .mast-id h1{margin-top:var(--s4);font-size:clamp(32px,9.6vw,48px);overflow-wrap:break-word}

  .site-header .wrap{min-height:56px;padding-top:var(--s2);padding-bottom:var(--s2)}
  .brand img{height:24px}
  .site-header .btn{font-size:14px;padding:11px 16px}
  .hide-sm{display:none}

  .stats{grid-template-columns:repeat(2,1fr)}
  .stat:nth-child(2n+1){border-left:0}

  .member-grid{grid-template-columns:1fr}

  /* The globe scales with the viewport, so scale the markers back up in its units */
  .marker .core{r:9}
  .marker .halo{r:18}
  .marker .pulse{r:15}
  .marker .badge-dot{r:11}
  .marker .tag-text{font-size:22px}
  /* No hover on touch, and these collide at 22px — the popover names them instead. */
  .marker .tag-agency{display:none}
  .marker .badge-text{font-size:15px}

  .panel-mast,.panel-body,.panel-actions,.panel-bar{padding-left:var(--s5);padding-right:var(--s5)}
  .panel-bar .crumb{left:var(--s5)}
  .panel-mast h2{font-size:24px;line-height:30px}
  /* Narrow enough that the geo facts wrap, which would leave a hairline dangling
     at the start of a line - space them instead. */
  .panel-mast .geo{gap:var(--s2) var(--s5)}
  .panel-mast .geo > span + span::before{display:none}

  /* Three columns will not hold at this width; two keeps the labels unwrapped */
  .footer-cols{grid-template-columns:repeat(2,1fr);gap:var(--s8) var(--s6)}
  .site-footer .footer-top{gap:var(--s9);padding-top:var(--s9);padding-bottom:var(--s9)}
  .footer-blurb{font-size:14px;line-height:22px}
  .footer-legal{margin-left:0;width:100%}
}

@media (max-width:479px){
  .footer-cols{grid-template-columns:1fr;gap:var(--s7)}

  /* "independents" is the longest word in the headline and it does not break:
     at the 48px floor it measures 308px against a 280px column at 320px wide,
     which pushed the whole page into a horizontal scroll. 42px clears it with
     room, and the clamp still governs from 480px up. */
  .t-display{font-size:42px}

  /* Mark and action have to hold one line down to 320px: both step down. */
  .site-header .wrap{gap:var(--s2)}
  .brand img{height:21px}
  .site-header .btn{font-size:13px;padding:10px 14px}

  /* "The APAC members" cannot hold one line at 36px here, and three words over
     two lines always strands one. Stepping the size down is the only fix that
     does not strand a word. */
  /* At 36px "The APAC members" overflows a 280px column by 7px and strands
     "members". 28px is the largest size that holds it on one line. */
  .t-h2{font-size:28px;line-height:1.2}

  /* Three controls plus a crumb will not fit 320px: the network prefix goes,
     since the panel is only ever reached from inside the network. */
  .panel-bar .crumb{font-size:11px;letter-spacing:.1em}
  .crumb-net{display:none}
  .panel-id{gap:var(--s3)}
  .panel-id .tile{width:44px;height:44px;font-size:14px}
  .panel-mast h2{font-size:21px;line-height:27px}
}


/* ==========================================================================
   15. Reduced motion
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  /* The colour is still restored on hover; only the fade to it goes. */
  .client-mark{transition:none}

  *{transition:none!important;animation:none!important}
  html{scroll-behavior:auto}

  /* The wipe and the lift are both transitions, so the rule above removes them.
     Hand these buttons back a plain instant colour change - otherwise the only
     users who lose the animation also lose every trace of hover feedback. */
  .btn-fill::before{display:none}
  .btn-primary:hover,.btn-primary:focus-visible{background:var(--purple-deep)}
  .btn-on-dark:hover,.btn-on-dark:focus-visible{background:var(--lavender)}

  /* Same principle for the underline: the WIPE is what this query objects to,
     not the underline. `*{transition:none}` above already removes the sweep, and
     because the hover state sets the full width directly the rule still arrives -
     instantly, from nowhere in particular. Nothing to add; recorded here so the
     next person does not "fix" it by hiding the line as well. */
}
