/* XINNACE public business card
   -----------------------------------------------------------------------
   Every colour, radius and typeface here comes from the company's design,
   injected as custom properties. This file owns structure, rhythm and
   behaviour; the company owns the look. --card-primary-ink and
   --card-surface-ink are computed server-side from luminance, so text stays
   readable whatever colour the company picks. */

*, *::before, *::after { box-sizing: border-box; }

/*
 * The type scale has to be applied here, not on the body.
 *
 * Every size on this card is expressed in rem, and rem resolves against the
 * root element — so scaling the body's font-size, which is where this used to
 * be, changed the body's own computed size and nothing else at all. The
 * slider moved and the card did not.
 */
html {
  -webkit-text-size-adjust: 100%;
  font-size: calc(16px * var(--type-scale, 1));
}

body.card-page {
  margin: 0;
  min-height: 100dvh;
  /* The page behind the card. Falls back to the card's own colour, which is
     what it was before there was a separate control — so a design that has
     never touched it looks exactly as it did. */
  background: var(--card-page-bg, var(--card-surface));
  padding: clamp(12px, 4vw, 40px) 16px calc(28px + env(safe-area-inset-bottom));
  display: grid;
  place-items: start center;
  color: var(--card-text-ink, var(--card-surface-ink));
  font-family: var(--card-font);
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* ---------- the XINNACE wordmark ----------
   On a card, XINN follows the company's primary colour and ACE stays the
   brand teal — the attribution is XINNACE speaking, in the customer's voice. */

.wordmark { white-space: nowrap; letter-spacing: normal; }
/* Not --card-primary directly: the server walks that colour far enough to
   stay readable on whatever surface it ends up on, and hands back the result.
   currentColor is the fallback, which is legible by definition. */
.wordmark-xinn { color: var(--card-wordmark-ink, currentColor); }
.wordmark-ace  { color: var(--card-wordmark-ace, #12B5AB); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- the card, and its two faces ---------- */

.stage { width: 100%; display: grid; justify-items: center; }

/*
 * The card is one object; only its masthead turns over.
 *
 * The surface, the radius and the shadow belong to the card itself now. They
 * used to sit on each face, which is what made the whole thing flip — the
 * face WAS the card. Moving them up means the block below the masthead stays
 * put while the identity above it changes, which is what a physical card
 * being turned in someone's hand does not do, but a screen can.
 */
.card {
  width: min(430px, 100%);

  /*
   * The card measures its own insides.
   *
   * Everything below used to size itself against the viewport - clamp(..., 7vw,
   * ...) and friends - even though the card stops growing at 430px. So the same
   * card drew a 320px masthead on a laptop and a 270px one on a phone, and the
   * designer's preview, sitting in an iframe of its own width, produced a third
   * answer. Three renderings of one object, none of them agreeing.
   *
   * As a container, 1cqw is one percent of the card, so the card looks the same
   * wherever it is drawn and scales only when the card itself is narrower than
   * 430 - which is the only time it should.
   */
  container-type: inline-size;
  container-name: card;

  perspective: 1800px;
  display: flex;
  flex-direction: column;
  background: var(--card-surface-bg);
  color: var(--card-text-ink, var(--card-surface-ink));
  border-radius: var(--card-radius);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(10, 16, 26, .06), 0 24px 60px -28px rgba(10, 16, 26, .45);
}

/* The flipping part: just the two mastheads, stacked in one grid cell so the
   block is as tall as the taller of them and nothing below it shifts. */
.faces {
  display: grid;
  transform-style: preserve-3d;
  transition: transform .62s cubic-bezier(.22, .78, .28, 1);
}
.card.is-flipped .faces { transform: rotateY(180deg); }

/* The settle after the opening flip.
 *
 * On the flip control, not on the card and not on the masthead it turns. The
 * card coming to rest is the wrong thing to draw the eye to — the point of the
 * sequence is that there is a second side and this button is how you get back
 * to it, so the movement belongs on the button. Nothing that did not flip
 * moves at all.
 *
 * The amplitude is larger than it would be on the card because the control is
 * small: a degree of rotation that reads clearly across a whole card is
 * invisible on a pill this size.
 *
 * .flip carries no transform of its own — its hover state only changes
 * opacity — so unlike the masthead there is nothing here for the animation to
 * fight. card.js still clears the class on any flip, because a control that
 * keeps wobbling after somebody has already used it is just noise. */
@keyframes flip-settle {
  0%   { transform: rotate(0deg); }
  20%  { transform: rotate(-3.2deg); }
  44%  { transform: rotate(2.1deg); }
  66%  { transform: rotate(-1.1deg); }
  85%  { transform: rotate(.45deg); }
  100% { transform: rotate(0deg); }
}
.card.is-settling .flip { animation: flip-settle .72s cubic-bezier(.36, .07, .19, .97) both; }

.face {
  grid-area: 1 / 1;
  display: flex;
  flex-direction: column;
  backface-visibility: hidden;
}

/* Both mastheads share one cell, so the block is as tall as the taller of
   them — the employee's, which carries a portrait as well as a name. Left
   alone the company's shorter band stopped early and left a void of card
   surface beneath it. The band fills the block instead and centres what it
   holds; for the employee side, whose content defines the height in the first
   place, this changes nothing. */
.faces .hero {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.face-back { transform: rotateY(180deg); }

/* backface-visibility hides the far face from view, but its links stay in
   the tab order and are still announced by screen readers — so a keyboard
   user tabs into contact details they cannot see. visibility:hidden removes
   it from both, and the delay lets it switch at the midpoint of the flip,
   while the face is edge-on and invisible anyway. pointer-events follows
   suit so nothing under the surface is clickable either. */
.face { transition: visibility 0s linear .31s; }
.face-back  { visibility: hidden;  pointer-events: none; }
.face-front { visibility: visible; pointer-events: auto; }
.card.is-flipped .face-front { visibility: hidden;  pointer-events: none; }
.card.is-flipped .face-back  { visibility: visible; pointer-events: auto; }

/* ---------- slide and fade, the other two transitions ----------

   These do not rotate, so the 3D context has to go with them: with a
   perspective still on the card, a translated face renders at a subtly wrong
   scale. Both work by cross-dissolving the two faces, which share one grid
   cell — so the incoming face must become visible immediately while the
   outgoing one only stops being focusable once it has finished leaving.
   That is the reason for two different visibility delays below rather than
   one shared rule. */

.transition-slide .card,
.transition-fade  .card { perspective: none; }
.transition-slide .faces,
.transition-fade  .faces { transform: none; transition: none; transform-style: flat; }

/* This one has to out-specify `.card.is-flipped .faces`, which is three
   classes deep. Without it the flip's half-turn still applies to the whole
   stack in slide and fade mode, and the back face renders mirrored —
   every word on it reversed. */
.transition-slide .card.is-flipped .faces,
.transition-fade  .card.is-flipped .faces { transform: none; }

/* --- none ---
   The two sides are still there and the control still turns the card; it just
   arrives rather than travels. Written out rather than folded into fade with a
   zero duration, because a zero-duration transition still schedules a frame and
   still fires transitionend, and the visibility delays below are what make the
   swap clean either way. */

.transition-none .card { perspective: none; }
.transition-none .faces,
.transition-none .card.is-flipped .faces { transform: none; transition: none; transform-style: flat; }

.transition-none .face {
  transform: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: none;
}
.transition-none .face-front,
.transition-none .card.is-flipped .face-back {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}
.transition-none .card.is-flipped .face-front {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* --- fade --- */

.transition-fade .face {
  transform: none;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .34s ease, visibility 0s linear .34s;
}
.transition-fade .face-front,
.transition-fade .card.is-flipped .face-back {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition: opacity .34s ease, visibility 0s linear 0s;
}
.transition-fade .card.is-flipped .face-front {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .34s ease, visibility 0s linear .34s;
}

/* --- slide ---
   Travel is deliberately small and paired with a fade. The faces are not
   clipped to the card — clipping would cut off its shadow — so a face that
   slid its full width would sail across the page instead of leaving it. */

.transition-slide .face {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(12%);
  transition:
    transform .42s cubic-bezier(.22, .78, .28, 1),
    opacity .28s ease,
    visibility 0s linear .42s;
}
.transition-slide .face-front,
.transition-slide .card.is-flipped .face-back {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateX(0);
  transition:
    transform .42s cubic-bezier(.22, .78, .28, 1),
    opacity .28s ease,
    visibility 0s linear 0s;
}
.transition-slide .card.is-flipped .face-front {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translateX(-12%);
  transition:
    transform .42s cubic-bezier(.22, .78, .28, 1),
    opacity .28s ease,
    visibility 0s linear .42s;
}

/* ---------- hero ---------- */

.hero {
  position: relative;
  background: var(--card-primary-bg);
  color: var(--card-primary-ink);
  padding: clamp(26px, 8.84cqw, 38px) clamp(20px, 6.98cqw, 30px) clamp(22px, 6.98cqw, 30px);
  isolation: isolate;
}
.shapes { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.shape  { position: absolute; display: block; }

.hero-body { position: relative; z-index: 1; }

/* The company lockup on a single-sided card.
 *
 * It had no rules at all when the markup arrived, so the logo drew at whatever
 * size the file happened to be and ran off the edge of the card — a wordmark
 * three times the width of the name underneath it.
 *
 * Sized well below the person on purpose. On this format the company is the
 * letterhead and the individual is the letter: big enough to be read and
 * recognised, small enough that a visitor still sees whose card they are
 * holding. Height rather than width is the constraint, because logos arrive in
 * every proportion there is and a wide one capped by width alone would sit a
 * quarter of an inch tall next to a square one filling the band. */
.lockup {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 18px;
  min-width: 0;
}
.lockup-logo {
  max-height: 34px;
  max-width: min(180px, 62%);
  width: auto;
  height: auto;
  display: block;
  object-fit: contain;
}
.lockup-name {
  font-family: var(--card-font-heading, var(--card-font));
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .82;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* A wide logo and a company name are competing for one line. Where the logo
   already carries the name there is nothing beside it and it may take the room;
   where it does not, it gives some back so the name sits on one line instead of
   wrapping into a ragged two. */
.lockup:has(.lockup-name) .lockup-logo { max-width: min(132px, 44%); }

/* Follows the hero. A lockup pinned left above a centred name would look like
   a mistake rather than a choice. */
.heroalign-centre .lockup { justify-content: center; }
.heroalign-right  .lockup { justify-content: flex-end; }

/* Align / Justify — where the photograph, name and title sit on the hero.
 *
 * Left is the default and the historical behaviour: this block adds the
 * centred option rather than changing anything that already exists.
 *
 * Scoped to .face-front deliberately. The company side has always been centred
 * by .hero-body-centred, which is a property of that face rather than a choice
 * — a logo above a company name reads centred or it reads like a mistake — so
 * this control governs the employee side and leaves the other alone. */
.heroalign-centre .face-front .hero-body { text-align: center; }
.heroalign-centre .face-front .portrait  { margin-inline: auto; }

.heroalign-right .face-front .hero-body { text-align: right; }
.heroalign-right .face-front .portrait  { margin-inline-start: auto; margin-inline-end: 0; }
.heroalign-right .face-front .name      { text-wrap: balance; }

/* The company face follows the same choice.
 *
 * It used to be centred and nothing else — .hero-body-centred is written into
 * the markup, and the designer said so out loud: "the company side stays
 * centred either way". That held while the two sides were the only two places
 * anything sat. Asked to align the logo and the company name, the honest answer
 * is that they take the setting like everything else, and the sentence in the
 * designer that promised otherwise comes out with it.
 *
 * Three classes deep so it out-specifies the single class in the markup. */
.heroalign-left  .face-back .hero-body { text-align: left; }
.heroalign-left  .face-back .logo      { margin-inline: 0; }
.heroalign-right .face-back .hero-body { text-align: right; }
.heroalign-right .face-back .logo      { margin-inline-start: auto; margin-inline-end: 0; }
/* Two words of a long name on one line and one on the next looks accidental
   when centred, where left-aligned it just looks like a line break. */
.heroalign-centre .face-front .name { text-wrap: balance; }

.portrait {
  width: 112px; height: 112px;
  margin-bottom: 18px;
  overflow: hidden;
  background: rgba(255, 255, 255, .16);
  border: 3px solid rgba(255, 255, 255, .38);
  display: grid; place-items: center;
}
.photo-circle  .portrait { border-radius: 50%; }
.photo-rounded .portrait { border-radius: 22px; }
.photo-square  .portrait { border-radius: 4px; }
.portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }
.portrait-initial {
  font-size: 2.6rem;
  font-weight: var(--heading-weight);
  color: var(--card-primary-ink);
  opacity: .9;
}

/* The three places set in the display face. For five of the six typefaces
   --card-font-heading is the same value as --card-font and this changes
   nothing; for Playfair it is what keeps the serif on the masthead and off
   the contact rows, where its hairlines would disappear. */
.name, .company-name, .portrait-initial { font-family: var(--card-font-heading, var(--card-font)); }

.name {
  margin: 0;
  font-size: clamp(1.55rem, calc(7.26cqw * var(--type-scale, 1)), 1.95rem);
  font-weight: var(--heading-weight);
  line-height: 1.12;
  letter-spacing: -.015em;
  text-wrap: balance;
}
.role { margin: 6px 0 0; font-size: 1rem; opacity: .92; }
.dept {
  margin: 3px 0 0;
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .72;
}
.logo { max-width: 190px; max-height: 82px; display: block; }

/* The company side is a masthead, not a personal introduction: there is no
   photo anchoring one edge, so a left-aligned logo with the name beneath it
   reads as though it slipped. Centring is what makes it look deliberate. */
.hero-body-centred { text-align: center; }

.hero-body-centred .logo { margin-inline: auto; }
.hero-body-centred .company-name { text-wrap: balance; }

.company-name {
  margin: 0;
  font-size: clamp(1.4rem, calc(6.70cqw * var(--type-scale, 1)), 1.8rem);
  font-weight: var(--heading-weight);
  letter-spacing: -.01em;
}

/* ---------- body ---------- */

.body {
  padding: clamp(20px, 6.05cqw, 26px) clamp(18px, 6.05cqw, 26px) 8px;
  display: grid;
  gap: 20px;
  align-content: start;
}
.density-compact .body { gap: 14px; padding-top: 18px; }
.density-roomy   .body { gap: 28px; padding-top: 30px; }

/* Both bios share one grid cell, so the block is as tall as the longer of
   them and the contacts below hold still through the flip. They cross-fade on
   the same class the masthead turns on.
   
   Every rule here is written at the same specificity — two classes — because
   the outgoing and incoming halves need different visibility delays, and a
   one-class rule would lose to `.bio-stack .bio` and silently take the wrong
   one. The incoming bio becomes visible at once and fades in; the outgoing
   one stops being visible only after it has finished fading. */
/* The row is as tall as the longer of the two bios, so the contacts below it
   never move. `align-items: start` keeps each bio only as tall as its own
   text, so the accent rule stops at the end of the sentence instead of
   running on through the reserved space. */
.bio-stack { display: grid; align-items: start; }
.bio-stack .bio {
  grid-area: 1 / 1;
  transition: opacity .3s ease, visibility 0s linear .3s;
}
.bio-stack .bio-employee { opacity: 1; visibility: visible; transition: opacity .3s ease, visibility 0s; }
.bio-stack .bio-company  { opacity: 0; visibility: hidden; }

.is-flipped .bio-stack .bio-employee { opacity: 0; visibility: hidden; transition: opacity .3s ease, visibility 0s linear .3s; }
.is-flipped .bio-stack .bio-company  { opacity: 1; visibility: visible; transition: opacity .3s ease, visibility 0s; }

/* One face, so the two bios stack instead of sharing a cell and cross-fading.
   Everything the two-sided version does — the overlap, the reserved height, the
   fade — exists to stop the contact rows moving as the card turns. Nothing
   turns here, so all of it comes off and they simply follow one another. */
.bio-stack-flat { display: grid; gap: 6px; }
.bio-stack-flat .bio {
  grid-area: auto;
  opacity: 1;
  visibility: visible;
  transition: none;
}
.bio-stack-flat .bio + .bio-label { margin-top: 10px; }

/* Whose words these are. Only rendered when both bios are on one face, where
   the question is real — see card.php. Sized well below the person's name: it
   is a signpost, not a heading competing with the masthead. */
.bio-label {
  margin: 0;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  opacity: .62;
  font-family: var(--card-font-heading, var(--card-font));
}

/* Only one bio was written. `display: none` rather than a hidden element:
   .body is a grid with a gap, so an emptied row would still cost its own
   height plus a second gap — a blank band exactly where the missing bio
   would have been. Taking the row out takes its gap with it. */
.card.is-flipped        .bio-stack-employee-only,
.card:not(.is-flipped)  .bio-stack-company-only { display: none; }

.bio {
  margin: 0;
  font-size: .96rem;
  line-height: 1.6;
  opacity: .82;
  padding-left: 14px;
  border-left: 2px solid color-mix(in srgb, var(--card-primary) 45%, transparent);
}

/* Both bios take the company's choice, and take the same one.
 *
 * The company's used to be centred here and the person's left, which was
 * defensible while the company's only ever appeared on its own centred face.
 * A single-sided card puts them one under the other, and then it is not two
 * treatments for two contexts — it is one column that cannot make its mind up.
 *
 * The accent rule follows the text rather than staying put. A left rule beside
 * centred text points at nothing, and beside right-aligned text it is on the
 * wrong side of the sentence entirely — so it moves with the alignment and
 * leaves when there is no edge for it to sit against. */
.bioalign-centre .bio {
  text-align: center;
  padding-left: 0;
  border-left: 0;
}
.bioalign-right .bio {
  text-align: right;
  padding-left: 0;
  border-left: 0;
  padding-right: 14px;
  border-right: 2px solid color-mix(in srgb, var(--card-primary) 45%, transparent);
}

/* The heading over each one goes with it. */
.bioalign-centre .bio-label { text-align: center; }
.bioalign-right  .bio-label { text-align: right; }


/* ---------- contact rows ---------- */

.contacts { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.contacts a {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 14px;
  padding: 11px 14px 11px 11px;
  border-radius: var(--btn-radius);
  text-decoration: none;
  color: inherit;
  background: color-mix(in srgb, var(--card-surface-ink) 4%, transparent);
  border: 1px solid color-mix(in srgb, var(--card-surface-ink) 9%, transparent);
  transition: background .16s ease, transform .16s ease;
}
.contacts a:hover { background: color-mix(in srgb, var(--card-surface-ink) 8%, transparent); }
.contacts a:active { transform: scale(.99); }
/* The badge keeps the brand surface behind it; the glyph takes the icon
   colour. Only where that colour can actually be seen on the badge — a company
   choosing a colour close to its own primary would otherwise knock its icons
   out of existence, and card_design_legible_on walks it back for them. */
.contact-icon {
  width: 40px; height: 40px;
  display: grid; place-items: center;
  border-radius: 50%;
  background: var(--card-primary-bg);
  color: var(--card-primary-ink);
}
/* The glyph, not the badge behind it.
 *
 * This used to set colour on .contact-icon, which is the circle — so the ink
 * took the ring with it wherever the ring is drawn in currentColor, and a
 * company choosing a colour for its icons found it had recoloured the shape
 * around them as well. The drawing inside is the icon; the circle is the
 * surface it sits on and belongs to the card's own colours.
 *
 * Two variables and not one, because these two rows have different things
 * behind them: a contact glyph is inside a disc filled with the primary, a
 * social glyph is on the card's own surface. One ink read against one
 * background could only ever be right for one of them. */
.contact-icon svg { color: var(--card-badge-ink, var(--card-primary-ink)); }
.socials a svg    { color: var(--card-icon-ink, var(--card-surface-ink)); }
.contact-text { display: grid; min-width: 0; }
.contact-label {
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .55;
}
.contact-value {
  font-size: .98rem;
  font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chev { opacity: .35; font-size: 1.4rem; line-height: 1; }

/* ---------- actions ---------- */

.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.actions .action-primary { grid-column: 1 / -1; }

.action {
  font: inherit;
  font-weight: 600;
  font-size: .92rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 13px 16px;
  border-radius: var(--btn-radius);
  cursor: pointer;
  text-decoration: none;
  border: 1px solid color-mix(in srgb, var(--card-primary) 34%, transparent);
  background: color-mix(in srgb, var(--card-primary) 10%, transparent);
  color: var(--card-text-ink, var(--card-surface-ink));
  transition: transform .16s ease, background .16s ease;
}
.action:hover  { background: color-mix(in srgb, var(--card-primary) 18%, transparent); }
.action:active { transform: scale(.98); }

.action-primary {
  background: var(--card-primary-bg);
  color: var(--card-primary-ink);
  border-color: transparent;
}
.action-primary:hover { background: var(--card-primary-bg); filter: brightness(1.06); }

.buttons-outline .action { background: transparent; }
.buttons-outline .action-primary {
  background: transparent;
  color: var(--card-text-ink, var(--card-surface-ink));
  border-color: var(--card-primary);
}
.buttons-solid .action {
  background: var(--card-primary-bg);
  color: var(--card-primary-ink);
  border-color: transparent;
}
.has-shadow .action-primary { box-shadow: 0 8px 22px -12px color-mix(in srgb, var(--card-primary) 90%, transparent); }

/* ---------- social row ---------- */

.socials {
  list-style: none;
  margin: 0; padding: 4px 0 0;
  display: flex; flex-wrap: wrap; gap: 10px;
  justify-content: center;
}
.social {
  width: 42px; height: 42px;
  display: grid; place-items: center;
  border-radius: 50%;
  color: var(--card-text-ink, var(--card-surface-ink));
  background: color-mix(in srgb, var(--card-surface-ink) 6%, transparent);
  border: 1px solid color-mix(in srgb, var(--card-surface-ink) 10%, transparent);
  transition: transform .16s ease, background .16s ease;
}
.social:hover { transform: translateY(-2px); }
/* iconshape-, not icons-. The two controls were sharing one prefix: icon
   STYLE emitted icons-brand / icons-mono / icons-outline, and icon SHAPE was
   written here as icons-rounded / icons-square — which the card never put on
   the body at all. So the shape select saved its value, the value survived
   every round trip, and nothing on the card ever changed. A dead control that
   remembers what you told it is worse than one that visibly does nothing. */
.iconshape-rounded .social { border-radius: 12px; }
.iconshape-square  .social { border-radius: 4px; }

/* Brand colours only when the company asked for them; otherwise the icons
   inherit the card's own ink, which keeps a busy row calm.
 *
 * On the glyph, and that is the whole of it. These used to set colour on the
 * anchor and let the drawing inherit it, which worked until the drawing was
 * given a colour of its own — an inherited value loses to a rule on the
 * element no matter how specific the ancestor's selector is, so LinkedIn blue
 * was being set and then quietly overwritten on every card. Choosing brand
 * colours did nothing at all.
 *
 * They win over the icon ink because they are the more specific answer to the
 * same question: a company that has asked for LinkedIn to be LinkedIn's blue
 * has said something about LinkedIn, and the ink setting is what it says about
 * everything else. A platform with no colour of its own still takes the ink. */
.icons-brand .social-linkedin  svg { color: #0A66C2; }
.icons-brand .social-facebook  svg { color: #1877F2; }
.icons-brand .social-instagram svg { color: #D62976; }
.icons-brand .social-whatsapp  svg { color: #25D366; }
.icons-brand .social-youtube   svg { color: #FF0000; }
.icons-outline .social { background: transparent; }

/* ---------- flip and footer ---------- */

.flip {
  font: inherit;
  font-size: .84rem;
  font-weight: 600;
  letter-spacing: .01em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* First thing in the body now, directly under the masthead it turns over,
     so the control sits next to the thing it controls. */
  margin: 0 auto;
  padding: 9px 18px;
  border-radius: 999px;
  cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--card-surface-ink) 16%, transparent);
  background: transparent;
  color: var(--card-text-ink, var(--card-surface-ink));
  opacity: .8;
  max-width: calc(100% - 40px);
}
.flip:hover { opacity: 1; }
.flip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The label names where the button will take you, and swaps on the same class
   the flip itself keys off — so it is correct before any script has run and
   stays correct if the script never does. */
.flip-to-employee { display: none; }
.card.is-flipped .flip-to-company { display: none; }
.card.is-flipped .flip-to-employee { display: inline; }

.attribution {
  margin-top: 16px;
  padding: 14px 18px calc(16px + env(safe-area-inset-bottom));
  text-align: center;
  font-size: .74rem;
  letter-spacing: .02em;
  /* Faded with a translucent colour rather than opacity. opacity applies to
     the element as a group, and nothing inside it can be more opaque than the
     group — which would drag the brand colours in the wordmark down with the
     surrounding text and leave them unable to recover. */
  color: color-mix(in srgb, var(--card-surface-ink) 55%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--card-surface-ink) 8%, transparent);
}
.attribution a { color: inherit; text-decoration: none; }
.attribution a:hover { text-decoration: underline; }

/* The credit and the cross-sell belong together on one line; the privacy
   notice sits under them on its own. Margin only on the separator — a rule
   broad enough to catch every span would also catch the three the wordmark
   is built from and pull XINN and ACE apart. */
.attribution-line { margin: 0; }
.attribution-sep { margin: 0 .35em; }
.attribution-legal { margin-top: 4px; }

/* ---------- QR sheet ---------- */

.qr-sheet {
  position: fixed; inset: 0; z-index: 40;
  display: grid; place-items: center;
  padding: 20px;
  background: rgba(8, 12, 18, .68);
  backdrop-filter: blur(3px);
}
.qr-sheet[hidden] { display: none; }
.qr-panel {
  background: #fff;
  color: #131820;
  border-radius: 18px;
  padding: 24px;
  display: grid;
  gap: 14px;
  justify-items: center;
  max-width: 320px;
  width: 100%;
}
/* --- switching between the two codes --- */

.qr-switch {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 4px;
  width: 100%;
  padding: 4px;
  border-radius: 999px;
  background: rgba(19, 24, 32, .07);
}
.qr-switch button {
  appearance: none;
  border: 0;
  border-radius: 999px;
  padding: 9px 12px;
  font: inherit;
  font-size: .9rem;
  font-weight: 600;
  color: rgba(19, 24, 32, .62);
  background: transparent;
  cursor: pointer;
  transition: background-color .14s ease, color .14s ease;
}
.qr-switch button:hover { color: #131820; }
.qr-switch button[aria-selected="true"] {
  background: #fff;
  color: #131820;
  box-shadow: 0 1px 2px rgba(19, 24, 32, .12), 0 2px 8px -4px rgba(19, 24, 32, .3);
}
.qr-switch button:focus-visible {
  outline: 2px solid var(--card-primary);
  outline-offset: 2px;
}

.qr-item { margin: 0; display: grid; gap: 12px; justify-items: center; width: 100%; }
.qr-item[hidden] { display: none; }
.qr-item figcaption {
  text-align: center;
  font-size: .86rem;
  opacity: .62;
  line-height: 1.45;
  max-width: 30ch;
}

/* One code at a time, so it takes the whole panel. Both codes render at the
   same size even though they hold different amounts of data and so have
   different module counts — a code that changed size when you switched would
   read as a glitch. The SVG carries a viewBox, so scaling up stays crisp. */
.qr-art {
  line-height: 0;
  width: min(100%, 264px);
}
.qr-art svg { width: 100%; height: auto; display: block; }
.qr-caption { margin: 0; font-size: .86rem; opacity: .6; text-align: center; }
.qr-panel .action { width: 100%; border-color: rgba(19,24,32,.2); background: rgba(19,24,32,.05); color: #131820; }

/* ---------- focus and motion ---------- */

a:focus-visible, button:focus-visible {
  outline: 2px solid var(--card-primary);
  outline-offset: 3px;
}
.hero a:focus-visible, .hero button:focus-visible { outline-color: var(--card-primary-ink); }

@media (prefers-reduced-motion: reduce) {
  /* The opening flip does not run at all under this — card.js checks the same
     query — but the animation is disabled here too so that a stylesheet that
     outlives that check cannot reintroduce it. */
  .card.is-settling .flip { animation: none; }
  .faces { transition: none; }
  .card.is-flipped .faces { transform: none; }
  .card .face-back { transform: none; visibility: hidden; }
  .card.is-flipped .face-front { visibility: hidden; }
  .card.is-flipped .face-back  { visibility: visible; }

  /* With every transition switched off, the swap is instant — so the two
     faces still have to end up at the right opacity and offset. */
  .transition-slide .face,
  .transition-fade  .face { transform: none; }
  .transition-slide .face-front,
  .transition-fade  .face-front,
  .transition-slide .card.is-flipped .face-back,
  .transition-fade  .card.is-flipped .face-back { opacity: 1; }
  .transition-slide .card.is-flipped .face-front,
  .transition-fade  .card.is-flipped .face-front { opacity: 0; }

  * { animation: none !important; transition: none !important; }
}

/* A name printed under a logo, when the logo is a symbol rather than a wordmark. */
.company-name-under { margin-top: 14px; font-size: 1.25rem; }
