/* ==========================================================================
   AMIN APAC — profile-services.css
   "What they do", the numbered service list. On profile-v2.html only.

   THIS REPLACED A PINNED STEPPER (client direction, 2026-09-01), and the
   reason is worth keeping because it is the trade any scroll-gated section
   makes. Measured, on M2.0's five services:

     stepper:  3780px of section (4.2 screens) to show 5 names and 3
               sub-services. 12 of 15 sub-services behind scroll.
     list:     ~820px to show 5 names and all 15 sub-services.

   A stepper is the right device when each item is one abstract word doing
   emotional work - the reference uses it for exactly that, four one-word
   claims, where the mechanism IS the message and nothing is being withheld.
   A services list is the opposite: the reader arrives with a question ("do
   they do crisis comms") and the answer has to be scannable, linkable and
   reachable by Cmd-F. Scroll-gating it works against the one job the section
   has.

   THE REFERENCE CHANGED (client direction, 2026-09-02). It was
   unitedcarriers.com/about's numbered row list; it is now the capability grid
   that Analogue Agency, Raw Materials and Aino Agency all set this content in,
   pulled from Mobbin. The reason is in the note on .svc-list below: our data
   has three levels of taxonomy and a row list can only show two of them
   without spending 900px.

   WHAT CARRIED OVER FROM THE REFERENCES, and what did not:
     - the practice area as a column heading over a rule. That rule is the only
       hairline left in the section, and it separates a heading from its own
       list rather than one row from the next.
     - the numerals. 01-05 signals completeness - "this is all of it" - which
       is worth more on a services list than on a values list. They moved from
       a rail to a prefix; see .svc-head.
     - NOT the dark ground several of them use. Ours is the last band before
       the dark footer, and inverting it would end the page in two dark bands
       with nothing between them. The footer is the switch.
     - NOT the boxed columns of Raw Materials. A card here would be the only
       card in the section.

   Deletable set: this file, js/profile-services.js and the one
   <section id="svc"> in profile-v2.html.
   ========================================================================== */

/* --------------------------------------------------------------------------
   THE BAND IS DARK (client direction, 2026-09-02)

   WHAT IT WAS: --surface-alt #F9F9FB. Measured against the white above and
   below it that is 1.05:1 - not a tone change, a rounding error. base.css's
   surface rule makes the tone change the separator, and this separator was
   invisible at arm's length, so the section had no ground at all rather than a
   quiet one.

   WHAT THE PAGE LOOKED LIKE. Ground sequence, top to bottom: dark video hero,
   white, white, white, 1.05:1 grey, white, 1.05:1 closer, dark footer. One
   dark moment and then roughly six thousand pixels of one surface. A page this
   long needs a second beat.

   WHY THIS SECTION AND NOT THE APPROACH BAND, which is the real question,
   because .approach-head is 84px against these names' 68 and the two were
   competing for the same job on the same white. The stage goes to the section
   the reader OPERATES. Every name here is a real button and the scroll drives
   them; the approach band is read, not used. So the approach band keeps the
   largest type and this one takes the ground - two currencies, one each, which
   is a cleaner split than both shouting on white.

   AND IT FIXES THE REVEAL'S CONNOTATION. Grey type on white is a UI convention
   that means DISABLED. Grey type on black is a stage convention that means NOT
   YET LIT. This section's whole conceit is names coming into light as the page
   scrolls; on white it was fighting that reading, on dark it uses it.

   --ink, NOT #000. It is the token the hero stage and the footer already
   carry, so this is the site's dark rather than a third one. 17.8:1 under
   white.

   EVERY TONE BELOW IS A TOKEN so the light band is one swap away, and the
   values are measured rather than picked:

     --svc-hi     #FFFFFF   18.1:1   headings, the lit name
     --svc-mid    #C9CCD2   11.3:1   the description
     --svc-int    #B4B8C0    9.1:1   the intro
     --svc-lo     #9A9EA8    6.8:1   eyebrow, sub-services
     --svc-unlit  #4C505A    2.2:1   the far end of the name ramp
     --svc-open   --purple    4.1:1  the open service - see the note below
     --svc-touch  --lavender 10.5:1  hover and focus - see the note below
     --svc-rule   22% white  2.0:1   the sub-service separator, composited

   Read off the rendered page, not calculated from the hex - the last two are
   composited values and the ramp's ends were sampled by forcing --t to 0 and 1
   on a live name.

   The unlit tone is deliberately under any floor for copy, and legitimately so
   for the same reason the light version's was - see the long note on .svc-name
   below. 2.24:1 here against the 1.9:1 it replaced, and the step from unlit to
   lit is 8.07:1 - wider than the light band managed, which is the reveal
   getting louder, not just different.
   -------------------------------------------------------------------------- */
.svc{
  background:var(--ink);
  padding:var(--sec) 0;

  --svc-hi:#FFFFFF;
  --svc-mid:#C9CCD2;
  --svc-int:#B4B8C0;
  --svc-lo:#9A9EA8;
  --svc-unlit:#4C505A;
  --svc-open:var(--purple);
  --svc-touch:var(--lavender);
  --svc-rule:rgb(255 255 255 / .22);
  /* Read by the shared eyebrow rule in css/profile-v-system.css - see the note
     there. Without it that rule's --gray lands at 1.43:1 on this ground. */
  --eyebrow-ink:var(--svc-lo);
  /* THE LIGHT BAND, kept whole rather than deleted: paste these six over the
     six above and delete the background line, and the section is exactly what
     it was.
       background:var(--surface-alt);
       --svc-hi:var(--black); --svc-mid:var(--slate); --svc-int:var(--gray);
       --svc-lo:var(--gray);  --svc-rule:var(--line);
       --svc-open:var(--purple); --svc-touch:var(--purple);
       --svc-unlit:color-mix(in srgb, var(--ash) 34%, var(--surface-alt)); */
}

/* THE GROUND IS FULL BLEED, THE CONTENT IS IN THE CONTAINER (client direction,
   2026-09-02). The inline padding was hero V's clamp, so the eyebrow sat on the
   same vertical as the agency name in the hero and "Our story" above it. The
   page has since moved the other way: every band except the hero is back in the
   site's 1400px container, and "Our story" is centred inside it, so matching
   the hero would now leave this the odd one out.

   .wrap's own two numbers, declared here rather than by adding the class,
   because the pin needs this element to stay the grid child of .svc-pin. */
.svc-wrap{
  width:100%;
  margin-inline:auto;
  padding-inline:var(--s6);
  /* CAPPED, THOUGH THE SECTION IS STILL FULL BLEED. The ground runs edge to
     edge; the content does not. Measured at 1920 the uncapped row was 1829px
     wide holding a service name, three short sub-services and a two-character
     numeral - 580px of it dead space between the name and its own
     sub-services. A hero name or a 76%-wide photograph can hold that measure.
     A three-item list cannot, and stretching one to fit does not make it look
     generous, it makes it look like a table with the wrong column widths.

     1240, ARRIVED AT BY MEASURING TWICE. 1500 was the first cap and it fixed
     the middle of the row but not the end of it: with the name track sized to
     its content the row's text finished at 910 while the hairline carried on
     to 1400, and 490px of rule with nothing under it draws the eye straight
     to the gap. The rule belongs to the list, so it has to end near the list.

     STILL LEFT-ANCHORED, and now anchored to the CONTAINER rather than to the
     hero's gutter - which is the only thing that changed here. The cap moved
     off this element onto .svc-header and .svc-list below, so this box can be
     .wrap's 1400 while the row keeps the 1240 measured above; centring the
     1240 instead would push the eyebrow 56px inboard of "Our story",
     "In practice" and the work grid, all of which start at the container's
     content edge. Below 1240 nothing changes. */
  max-width:1400px;
}

/* The 1240 cap, moved here from .svc-wrap so the wrap can be the container.
   Same number, kept because it is still the widest this band should ever get.

   CENTRED, NOT LEFT-ANCHORED - and that is a correction, not a preference
   (client direction, 2026-09-02). It read "left-anchored, so the row starts on
   the container's content edge with every other band on the page", which was
   correct while this section was a list of hairline rows with a numeral in the
   left margin. It is not that any more: the header sets its own contents to
   text-align:center and the stack below caps at 900 with margin-inline:auto.

   So the cap was doing something nobody asked for. Measured at 1400: the wrap
   gives a 1352px content box, the header took 1240 of it flush left and
   centred its type on 644, while the stack centred on 700. Two centred blocks
   on two different axes, 56px apart - which is exactly far enough to look like
   a mistake and not far enough to look like a decision.

   Both boxes now centre in the same container, so both axes land on 700. */
.svc-header,
.svc-list{ max-width:1240px; margin-inline:auto; }

/* THE LIST FILLS THE HEADER'S WIDTH. IT USED TO SHRINK TO ITS ROWS AND THAT
   WAS THE WRONG FIX (client direction, 2026-09-02).

   The reasoning behind the shrink was sound on its own terms - measure both
   tracks, then size the list to the widest row, so no hairline runs past the
   content it belongs to. But it was solving for one row in isolation and the
   reader does not see one row. Measured at 1440 it left the list 880px wide
   under a 1240px header, so the rules stopped 360px short of the intro
   paragraph directly above them. Two content widths in one section reads as a
   fault, and it is the first thing anyone looking at the section noticed.

   ONE MEASURE FOR THE WHOLE SECTION. The eyebrow, the heading, the intro and
   the grid all end inside the same vertical. */

/* --------------------------------------------------------------------------
   01. The header block
   Heading left, intro right, eyebrow spanning both as its own row. The same
   arrangement, and the same reasoning, as .approach-grid directly above it:
   spanning puts something in the top-left corner so the heading is not the
   first thing to arrive out of empty space, and it starts both columns on
   row 2 so they align to each other rather than one sitting under a label
   the other does not have.
   -------------------------------------------------------------------------- */
/* 1.3 : 1, NOT 1 : 1. The heading is display type that wants two lines; the
   intro is one sentence. Equal columns gave the heading three lines and the
   intro two - the same imbalance, and the same fix, as .approach-grid's
   1.15/.85 directly above. */
.svc-header{
  display:grid;
  grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
  column-gap:clamp(32px, 5vw, 88px);
  align-items:end;
  /* SMALL, BECAUSE THE RUNWAY IS ALREADY DOING THIS JOB. .svc-list carries
     12vh of top padding as the scrub's pace control, and 60px of header margin
     on top of it measured 196px between the intro and the first name - the
     header floating away from the stack it introduces. 24 + the runway lands
     around 150, which is the pause the section wants and not a gap. */
  margin-bottom:clamp(16px, 2vh, 28px);
}
/* THE MARGIN HERE WAS DEAD. --s10 was declared and then overridden to 0 by
   the centred-header rule further down this file, so 40px was written and 0
   applied - the first line anyone tuning this gap would have edited is the
   one that does nothing. The gap belongs to .svc-header's row-gap now, and
   the colour belongs to base.css's .eyebrow: this rule and
   .approach-eyebrow's were the same four declarations, and the copy carried
   --ash into two bands while the story band kept --gray.
   See css/profile-v-system.css. */
.svc-eyebrow{
  grid-column:1 / -1;
  margin:0;
}
/* RAISED OFF .story-head's 34/42, WHICH IT USED TO SHARE. The service names
   below are display type now, and a 34px section heading over a 40px list
   item is an inverted hierarchy - the label reading smaller than the things
   it labels. The scale holds a clear gap at every width: 60/40 at 1920,
   52/36 at 1440.

   That it no longer matches .story-head is deliberate rather than drift. The
   story band is a two-column section whose heading sits beside a measure of
   body copy; this one is full bleed and closes the page. The two are not the
   same job at the same width, and forcing one number across both was what
   produced the inversion. It still sits clearly under .approach-head's 84px
   display step, so the page reads hero > approach > svc > story. */
.svc-h2{
  margin:0;
  /* THE CEILING IS REACHED AT ~1278px OF VIEWPORT, WHICH IS DELIBERATE AND
     IS THE RULE FOR EVERY FLUID SIZE IN THIS FILE. The measure stops growing
     at 1240; a vw-based size that carries on past it puts more type into a
     column that is no longer getting wider, which is exactly what happened
     here - at 1920 a 60px heading broke into three lines in a 540px column.
     Type and measure have to stop together. */
  font-size:var(--pf-h2);
  line-height:1.1;
  font-weight:500;letter-spacing:-.018em;
  color:var(--svc-hi);
  text-wrap:balance;
}
/* ON THE PROSE STEP NOW. It ran clamp(15,1.3vw,17) at a 1.6 ratio, which was
   a fourth body size and a fifth leading ratio in a page that declares one of
   each. It is a lede - base.css sets .lede at 18/27 in --gray - so that is
   what it is, and it stops shrinking to 15 on a tablet where it is the only
   sentence explaining how the stack below it works. */
.svc-intro{
  margin:0;
  font-size:var(--pf-body);line-height:var(--pf-line);
  color:var(--svc-int);
  max-width:52ch;
}
.svc-intro:empty{display:none}

/* --------------------------------------------------------------------------
   02. The stack
   One centred column of display type. Every service is a name; the open one is
   black and shows its sub-services, the rest sit back in a pale grey.

   Cut against Klarna's "Explore our offering", which the client sent as the
   target: mobbin.com/sites/sections/2dcd9862-9787-411f-89ae-42a8465f2be6

   THIS IS THE FOURTH LAYOUT FOR THIS SECTION AND THE FIRST THAT IS ABOUT THE
   NAMES. The row list, the capability grid and the plate variant all treated
   the sub-services as the payload and the name as its label - which is why
   each of them spent a column, a track or a picture slot solving where to put
   fifteen short strings. This inverts it: the five service names ARE the
   section, at a size that says so, and the sub-services are what one of them
   says when you ask.

   WHAT THAT COSTS, AND IT IS THE SAME COST THE PINNED STEPPER WAS DELETED FOR.
   Only the open service's sub-services are on screen: twelve of fifteen are
   behind an interaction again. The difference is what is being withheld. The
   stepper hid the NAMES - a reader could not see that crisis work existed
   without scrolling four screens. Here every name is visible, at the largest
   type on the page, from the first frame; what is behind a click is the
   detail under one of them. A reader who arrives asking "do they do crisis
   comms" is answered without touching anything.

   The mitigations are in the markup rather than here: real <button>s with
   aria-expanded and aria-controls, and every panel present in the DOM. See
   js/profile-services.js.

   IT OPENS ON SCROLL (client direction, 2026-09-02). The reference is not a
   click accordion - each name opens as it reaches the middle of the screen and
   closes as it leaves, so an ordinary scroll through the section shows every
   service in turn without the reader doing anything. That materially changes
   the reachability argument above: the twelve sub-services behind an
   interaction are behind SCROLLING, which the reader is doing anyway, rather
   than behind five clicks they have to decide to make. The buttons stay, for
   keyboard and for anyone who wants to go back to one.

   NOTHING IS PINNED. The reference holds its stack while the page scrolls
   under it; this does not, because a pinned section here is the stepper that
   was deleted from this exact slot for the reason at the top of this file.
   The effect survives without it - what the reader loses is the section
   staying still, which is the part that was doing the harm.

   CENTRED, HEADER INCLUDED. The reference centres its heading over the stack,
   and a centred stack under a left-aligned two-column header reads as two
   sections. Section 01 is overridden below rather than rewritten, so putting
   the header back to left-aligned is deleting one rule.
   -------------------------------------------------------------------------- */
/* The header, re-centred for this layout. One column, so the intro sits under
   the heading rather than beside it. */
/* THE HEADER'S RHYTHM, MEASURED AND THEN FIXED. It ran eyebrow -> 52px -> h2
   -> 20px -> intro, which puts the LABEL further from the heading than the
   sentence that explains it. An eyebrow belongs to the heading directly under
   it and should be the tightest gap in the block, not the loosest.

   Now 24 and 32: the row gap alone carries the eyebrow, and the intro takes a
   little more so the sentence reads as a separate thought rather than a third
   line of the heading.

   --s6, not --s5. 24 is the gap under the eyebrow in the story and approach
   bands too - it was 24 / 40 / 20 across the three, one label with three
   spacings. See css/profile-v-system.css. */
.svc-header{
  grid-template-columns:minmax(0,1fr);
  justify-items:center;
  text-align:center;
  row-gap:var(--s6);
}
.svc-intro{margin-top:var(--s2)}
/* The intro is one sentence and it is centred: it needs a measure or it runs
   the full 1240 and reads as a rule of text rather than a caption. */
.svc-intro{max-width:52ch}

/* 900, not 1240. The stack is display type and its longest name -
   `PR & Digital Campaigns` - sets at about 700px at the ceiling below; a
   1240px box round it would leave the whole thing floating in the middle of a
   measure it never touches. */
.svc-list{
  max-width:900px;
  margin-inline:auto;
  text-align:center;
  /* A SCROLL RUNWAY, AND IT IS THE HONEST WAY TO SLOW THE SECTION DOWN
     (client direction, 2026-09-02).

     The scrub divides the list's own box into one band per service, so how
     fast the stack moves is decided by how tall that box is - five services in
     a 700px box change every 140px of scroll, which is about one wheel notch
     each. Padding the box is therefore not decoration here, it is the pace
     control, and it slows the section without lying to the measurement the way
     an arbitrary multiplier in the script would.

     12vh top and bottom takes m2's box from ~700 to ~940 at a 1000px viewport,
     which is a third more scroll for the same five services. It also buys the
     first and last name room to be centred on the focus line, which they could
     not be when the box ended at their own edges.

     ONLY WHERE THERE IS A SCRUB TO PACE. Ten of the eleven members are driven
     from `tags`: names with no description and no sub-services, so nothing
     opens, nothing scrubs and the runway would be 240px of empty ground above
     and below a plain list. :has() a real button is exactly the condition the
     script uses to decide whether to run at all. */
}
.svc-list:has(button.svc-toggle){
  padding-block:clamp(48px, 12vh, 150px);
}

/* THE PRACTICE AREA BAND AND THE NUMERAL ARE GONE (client direction,
   2026-09-02), and the rules for both went with them rather than being left
   dormant. Both were inherited from the numbered row list this section used to
   be:

     the band  put a mono line between two names and broke one stack into
               three short ones. The reference has a single run.
     the num   read as an index down a rail; centred over a 68px word, five of
               them were five small marks floating up the middle.

   `group` is still in js/data.js - it is the member's own taxonomy and no
   layout owns it. Nothing in this section reads it today.
   -------------------------------------------------------------------------- */

/* AIR BETWEEN THE NAMES (client direction, 2026-09-02). They ran with no gap
   at all for one round - the reasoning was that a solid run reads as one object
   with a hole cut in it - and at 68px with 1.06 leading that was simply tight:
   the descenders of one name almost touched the caps of the next, and the two
   greys either side of the open one bled into each other.

   FLUID, BECAUSE THE NAMES ARE. The type runs 34px to 68px across the range;
   a fixed gap that suits the ceiling is a hole at the floor. 1.4vw tracks the
   same axis the font-size clamp does. */
.svc-caps{
  list-style:none;margin:0;padding:0;
  display:flex;flex-direction:column;
  row-gap:clamp(14px, 1.4vw, 30px);
}
.svc-cap{display:block}

/* THE WHOLE NAME IS THE BUTTON. Not an icon beside it, not a chevron at the
   end: at this size the name is the only target anyone will aim at, and a
   35px hit area next to a 68px word is a target people miss. Reset to nothing
   - a <button> arrives with a border, a ground, a font and a text-align, and
   every one of those is wrong here. */
.svc-toggle{
  -webkit-appearance:none;appearance:none;
  display:block;width:100%;
  /* 4px, not --s2. The names are their own hit area at 68px and 1.06 leading;
     any more padding here opens gaps in a run that is meant to be tight. */
  margin:0;padding:4px 0;
  border:0;background:none;
  font:inherit;color:inherit;text-align:center;
  cursor:pointer;
}
.svc-toggle:focus-visible{
  outline:2px solid var(--purple);
  outline-offset:4px;
}

.svc-name{
  display:block;
  margin:0;
  /* One step under .approach-head's ceiling. That band is the page's statement
     and this is its list; setting them at the same size would make the reader
     weigh them the same. */
  font-size:var(--pf-d2);
  line-height:1.06;
  font-weight:600;
  letter-spacing:-.02em;
  /* A CONTINUOUS RAMP, NOT THREE STEPS (client direction, 2026-09-02).

     It ran as three fixed tones keyed to a data-d of 0, 1 or 2 - black,
     mid, pale - which is what the reference's stills show. In motion it was
     wrong: five names all changed tone on the same frame, so scrolling read as
     a slideshow of three states rather than as something being moved through.

     --t is now a real number per name, 1 where it is centred on the focus line
     and 0 two names away, written every frame by the scrub. The mix does the
     rest, so a name darkens as it approaches and lightens as it leaves and no
     two of them are ever on the same frame boundary.

     --t DEFAULTS TO 1, NOT 0, so the stack is fully lit if the scrub never
     runs at all - white now that the ground is dark, and that fallback is the
     reason the ramp is written lit-first. A default of 0 would leave a
     script-less page showing five
     names at 2.2:1 with no way to light them.

     THESE CONTRASTS ARE DELIBERATE AND THEY ARE NOT BODY TEXT. The far end of
     the ramp measures 2.2:1 on --ink, under any floor for copy. It is
     legitimate HERE and nowhere else on the page because these five words are
     a CONTROL and a position indicator: every one is a real button, scrolling
     brings each to full --svc-hi in turn, and hover or focus does it
     immediately. Nothing is only ever readable at 2.2:1.

     NO transition ON THE COLOUR. The value already changes every frame with
     the scroll; a transition on top of that is a second, slower animation
     chasing the first, and it makes a fast scroll look like it is lagging. */
  color:color-mix(in srgb,
    var(--svc-hi) calc(var(--t, 1) * 100%),
    var(--svc-unlit));
}
/* --------------------------------------------------------------------------
   HOVER AND FOCUS ARE PURPLE, THE SCROLL IS WHITE (client direction,
   2026-09-02, after testing --purple on the ramp itself)

   THE ASK WAS PURPLE ON THE HIGHLIGHTED NAME and the ramp is the wrong place
   for it, measurably. This reveal is a VALUE ramp - names get brighter as they
   arrive - and against the #4C505A unlit end:

     white              8.07:1     what ships
     --lavender         4.67:1     workable, 42% of the step gone
     --purple           1.82:1     the effect stops reading

   At 1.82:1 the mid-ramp names came out a muddy violet-grey, because the mix
   has to travel from a blue-grey to a saturated violet at about the same
   lightness and passes through sludge on the way. Rendered and looked at, not
   assumed. --purple also drops the lit name from 18.1:1 to 4.1:1 - still AA
   for type this size, but it makes the loudest thing in the section the
   dimmest thing on the page.

   THE REJECTION WAS TOO BROAD, AND THIS IS THE CORRECTION (same day, client
   pushed back). The measurement above is about the RAMP - a continuous mix
   from the unlit grey to the lit colour - and it rules purple out of THAT and
   nothing else. The OPEN service is not a ramp position. It is a discrete
   state on exactly one item at a time, so there is no interpolation to travel
   through mud, and 4.1:1 on 68px display type clears AA large (3:1)
   comfortably. Rendered side by side before committing.

   The first fix put purple on hover alone, and that was worse than it looked
   on paper: hover does not exist on touch, never appears in a screenshot or a
   review deck, and only shows on a member whose services carry descriptions -
   one of the eleven today. The brand colour was effectively nowhere.

   SO THERE ARE NOW THREE STATES AND EACH HAS ITS OWN INK:

     the scroll brings a name toward   --svc-hi     white,      18.1:1
     the section OPENS one at          --svc-open   --purple,    4.1:1
     the pointer or keyboard touches   --svc-touch  --lavender, 10.5:1

   THE RAMP IS UNTOUCHED, which is the whole point of doing it this way: the
   reveal keeps its full 8.07:1 white travel and the brand colour lands on top
   of it as a state, not inside it as a gradient.

   TOUCH BEATS OPEN, deliberately. button.svc-toggle:hover is (0,3,1) against
   .svc-cap.is-open's (0,3,0), so hovering the open service lifts it to
   lavender rather than leaving it purple. Pointing at something should always
   answer, including when it is already the active one.

   --lavender AND NOT --purple, on the dark ground. --purple is 4.1:1 here
   against --lavender's 10.5:1, and a hover state that is DIMMER than the
   scroll state it interrupts inverts the hierarchy - a deliberate act should
   never read quieter than an automatic one. On the light band the pair swaps:
   --purple is 4.4:1 on white where --lavender is 1.4:1 and unusable, which is
   why --svc-touch is a token with both values recorded above.

   base.css's colour rule is satisfied rather than bent: purple marks CONTROLS,
   blue marks links, and every one of these names is a real button.

   THE FOCUS RING STAYS --purple. It is --focus-c, the site-wide ring that
   circles buttons and links alike, and it measures 4.0:1 on this ground -
   clear of the 3:1 floor for a non-text indicator. Recolouring it here would
   make one section's focus look like a different system's.
   -------------------------------------------------------------------------- */
/* button.svc-toggle, NOT .svc-toggle. A member with no descriptions on its
   services renders each name as <div class="svc-toggle" role="heading"> - the
   same class, deliberately, so one rule sets the type for both - and a bare
   .svc-toggle:hover therefore offered a hover state on a heading that does
   nothing. It happened to look right only because .svc-cap.is-plain's rule
   sits later in this file at equal specificity and won on source order.
   Naming the element makes it impossible rather than lucky: the div can never
   match, whatever order these rules end up in. */
.svc-cap.is-open .svc-name{color:var(--svc-open)}

button.svc-toggle:hover .svc-name,
button.svc-toggle:focus-visible .svc-name{color:var(--svc-touch)}

/* THE HEIGHT IS DRIVEN BY THE SCRIPT, and the fashionable CSS-only version was
   tried first and does not work here.

   `grid-template-rows: 0fr -> 1fr` is the usual answer to "height:auto is not
   animatable", and it collapses to nothing only when the grid container has a
   definite block size. This one does not - it is a block in a flow-sized
   column - so flexible tracks are sized to their max-content contribution
   instead. Measured, all three of `0fr`, `minmax(0, 0fr)` and
   `minmax(0px, 0fr)` resolved to 48px, the panel's full height, with the panel
   closed. The trick was silently doing nothing.

   A guessed max-height is the other usual answer and it is worse: it eases at
   the wrong speed for every panel shorter than the guess, and these panels run
   from one line to three depending on the member.

   So the script measures and writes a real height, and hands the panel back to
   `auto` once the transition has finished - which is what keeps an open panel
   correct through a resize or a font swap. See js/profile-services.js.

   THE CLOSED HEIGHT IS HERE, NOT THERE, so a panel is closed before a single
   frame of script has run. */
.svc-panel{
  height:0;
  overflow:hidden;
  opacity:0;
  /* .55, up from .4. The name above it now changes tone continuously with the
     scroll rather than snapping, and a panel that finished well before that
     movement settled was the one thing left in the section that read as a
     step. The opacity trails the height slightly so the copy arrives into a
     box that is already most of the way open. */
  transition:height .55s var(--ease), opacity .4s var(--ease) .1s;
}
.svc-cap.is-open .svc-panel{opacity:1}

/* THE DESCRIPTION. One sentence, and it is the only thing in this section
   that is prose - which is why it takes a measure rather than the stack's full
   900px. 46ch at 19px lands around 60 characters a line, inside the 60-75 rule
   at the head of css/base.css, and it centres under a name that is often
   wider than it is.

   BACK ON THE PROSE STEP. It was 19/29, argued as "up a step from the 17px
   body" so it would not read as a caption under 68px display type - which is
   a real concern and the wrong instrument for it. 19/29 was a fifth body size
   at a sixth leading ratio, one pixel and one ratio off the page's prose, and
   at that distance it does not read as a step up: it reads as the same size
   set slightly wrong. What actually separates this sentence from a caption is
   the 46ch measure, the centring and the 24px of air above it, all of which
   are unchanged. 18/27 is the page's prose and this is prose. */
.svc-desc{
  max-width:46ch;
  margin:0 auto;
  padding-block:var(--s6) var(--s3);
  font-size:var(--pf-body);line-height:var(--pf-line);
  color:var(--svc-mid);
}

.svc-items{
  list-style:none;margin:0;padding:0;
  /* Inline, separated, and centred - a stacked bullet list under a 68px name
     would put four lines of 15px type in the middle of the composition and
     stop the stack reading as a stack. The separator is drawn by CSS so the
     list stays a list for a screen reader. */
  display:flex;flex-wrap:wrap;justify-content:center;
  gap:var(--s2) var(--s5);
  /* Tight to the description above it - they are one answer, not two - and
     clear of the next name below. */
  padding-block:var(--s3) var(--s8);
  /* 16/24, the meta step. It was 16/25 - a 1.5625 ratio in a page where every
     other prose block is 1.5 - which is a difference nobody sees on one line
     and everybody feels across a wrapped row. */
  font-size:var(--pf-meta);line-height:var(--pf-meta-line);
  /* --gray, NOT --ash, and the token names the reason. base.css sets --ash as
     "placeholder / inactive" and these are the opposite of that: the concrete
     service names, the most searchable words in this section and the ones a
     client scans for. Contrast was never the problem - #4A4E57 is 7.93:1 on
     this ground - the RANK was. --gray is 12.02:1 and adds no hue, so the row
     gains standing without gaining a colour.

     Still one step behind the description above it. The order is right: name,
     then the member's own sentence, then the list that itemises it - and it
     survives the move to the dark ground unchanged in rank: --svc-mid is
     11.1:1 and --svc-lo is 6.6:1, the same one-step gap in the same order. */
  color:var(--svc-lo);
}
.svc-items li{position:relative}
.svc-items li + li::before{
  content:"";
  position:absolute;left:calc(var(--s5) / -2);top:.7em;
  width:3px;height:3px;border-radius:50%;
  /* THE SEPARATOR WAS EFFECTIVELY INVISIBLE, and it is the only thing in this
     row doing structural work - it is what says three items rather than one
     long phrase. --line is #E2E2E2, which is 1.23:1 against --surface-alt: not
     a faint dot, an absent one. A row that looked flat looked that way because
     its punctuation was missing, not because its words were the wrong colour.

     #B9B9C2 is 2.14:1 - visible as a mark, and still far quieter than the
     16px labels it divides (12.02:1), which is the relationship a separator
     should have to the things it separates.

     A TOKEN RATHER THAN --line, deliberately. --line is the site's hairline for
     rules and card feet, where 1.23:1 is correct: a 1px line spanning 400px is
     legible at a contrast a 3px dot is not. Repainting the token would touch
     every border on the site to fix one dot.

     ON THE DARK GROUND it is rgb(255 255 255 / .22), which lands at 2.3:1 -
     the same relationship the #B9B9C2 above had to --surface-alt, inverted.
     Both values live on .svc so the light band is one swap away. */
  background:var(--svc-rule);
}
.svc-items:empty{display:none}

/* A SERVICE WITH NOTHING BEHIND IT IS NOT A CONTROL. Members driven from
   `tags` have names and no sub-services, so there is nothing to open - the
   script renders those without a button, and they sit at full black as a
   plain stack of names. Pretending otherwise would give the reader five
   things to click that do nothing. */
.svc-cap.is-plain .svc-name{color:var(--svc-hi)}

/* --------------------------------------------------------------------------
   03. The reveal
   Decoration on top of a document that is already complete: every service is
   in the DOM, at full contrast, in the accessibility tree and findable by
   Cmd-F before a single frame runs.
   -------------------------------------------------------------------------- */
/* Only a grid the script has claimed starts hidden, so a script that never
   runs - or throws - leaves a finished grid rather than an invisible one. */
/* --dim is the hover fade above; 1 whenever nothing is dimming. Both states
   below read it, so a service that has arrived lands on whatever the pointer
   is asking for rather than on a hard 1 that the dim then has to out-specify. */
.svc.is-armed .svc-cap{
  opacity:0;
  transform:translateY(12px);
  transition:opacity .5s var(--ease), transform .5s var(--ease);
  transition-delay:calc(var(--r, 0) * 60ms);
}
.svc.is-armed .svc-cap.is-in{opacity:1;transform:none}

/* --------------------------------------------------------------------------
   04. Narrow
   The stack needs almost nothing here - it is one centred column at every
   width and the name size is already fluid.
   -------------------------------------------------------------------------- */
@media (max-width:640px){
  /* A name that wraps to two lines at 34px needs its panel closer to it than a
     one-line name does, or the open service reads as detached from its own
     heading. */
  .svc-desc{padding-block:var(--s5) var(--s2);font-size:17px;line-height:26px}
  .svc-items{padding-block:var(--s2) var(--s6)}
}

/* --------------------------------------------------------------------------
   05. Reduced motion
   The stagger goes and nothing else does, because nothing else was motion.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion:reduce){
  .svc.is-armed .svc-cap{opacity:1;transform:none;transition:none}
  /* The panel still opens and closes - that is the control working, not
     decoration - it just stops sliding. */
  .svc-panel{transition:none}
}
