/* /about/ — the trust dossier.
   ---------------------------------------------------------------------------
   This page exists to answer one question: is it rational to let a stranger take
   control of my PC, erase Windows, and be paid up front with no refund? Four
   blocks, one objection each, in the order a buyer raises them - who are you,
   what happens to my machine, what stops you doing worse, and what if it goes
   wrong. Anything that did not answer one of those was cut rather than kept for
   completeness: a trust page loses more to padding than to being short.

   Shared, not rebuilt: .section, .container, .eyebrow, .section-head,
   .section-sub, .card, .btn and the .reveal choreography all come from
   prospira.css and landing.css. Only what this page genuinely does not have
   already is written here, under ab-. */

/* --- the opening statement ------------------------------------------------
   Centred, in the end. Left alignment was the plan - a record you read rather
   than a page that sells - but on screen it put 760px of text against 440px of
   nothing with no counterweight, and read as an unfinished layout instead of a
   deliberate one. Every other block on this page is centred; the odd one out was
   not earning it. */
/* No `isolation: isolate` here, deliberately. It made the hero a stacking
   context, which put the glow ABOVE the following section - so the glow had to
   stop dead 6px short of it rather than fade across the join. Without it the
   glow can sit at z-index -1, behind every section's text, and hand over softly. */
.ab-hero {
  position: relative;
  /* Measured against the other three heroes at 1440 and at 375, gap from the
     bottom of the nav to the first line of hero text:

         /            138        /utility/     64
         /services/    88        /about/  was 177 desktop, 117 mobile

     The other three all pull their hero UP under the nav (-116 / -78 / -78) and
     pay for it with a large padding-top; this one sits flush at 0 and was the
     only outlier, starting 89px lower than /services/ and drifting between
     widths on top of that. Pulling the section up is not the fix here - the glow
     already runs under the nav via its own -170px, and moving the section would
     shift the box every glow offset is measured from. Trimming the padding gets
     the same result and touches nothing else.

     No bottom padding: .section already carries margin-top: var(--section-gap)
     = 136px, so anything here would stack on top of it. */
  padding: clamp(84px, 6.6vw, 96px) 0 0;
}
/* The 720px cap moved off this wrapper and onto the prose inside it. It used to
   sit here, which also capped the pill row - and three pills need ~700px of pill,
   so they broke to two rows while 400px of container sat unused either side.
   Prose wants a measure; a row of chips does not. */
.ab-hero-in { position: relative; z-index: 1; text-align: center; }
.ab-hero-in .ab-title { max-width: 720px; margin-inline: auto; }

/* --- the backdrop ----------------------------------------------------------
   "Pulse bar slitted glass" from the 21st.dev gradient builder: six vertical
   bars whose heights follow a ramp envelope and dissolve into the backdrop, an
   EQ that breathes. Drawn by js/hero-bars.js - the builder's own CSS export says
   the variable heights need a canvas, and it is right.

   It replaced a static radial "Horizon Glow" that lived here before. The bars
   earn the swap by rhyming with the artwork in the deal cards further down the
   page, which is also a field of vertical columns - the page now opens and
   closes on the same shape.

   The palette is NOT the source's warm Navy/Coral/Ibis/Cream. See hero-bars.js.

   This wrapper only positions and quiets it: the canvas paints at full strength
   and everything that decides how loud it is on the page lives here, so it can
   be tuned without touching the maths. */
.ab-hero-bg {
  position: absolute;
  left: 0;
  right: 0;
  top: -170px;
  /* Only 46px past the hero, not 150. The recipe puts each bar's colour at its
     FOOT, and the feet always land on the canvas floor - so a floor sitting
     150px below the hero put every strong pixel inside the masked-out tail. The
     bars were there and invisible. Now the feet land at the hero's own bottom
     edge and the 46px is just room for them to fade into the gap. */
  bottom: -90px;
  z-index: -1;
  pointer-events: none;
  /* Gone before the section below starts, and faded in from under the nav, so it
     arrives and leaves without an edge - the same handover the radial did. */
  -webkit-mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 74%, transparent 100%),
    linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
  mask-image:
    linear-gradient(180deg, transparent 0%, #000 22%, #000 74%, transparent 100%),
    linear-gradient(90deg, transparent 0%, #000 10%, #000 90%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
/* The opacity lives on the CANVAS, not on the wrapper. Put it on the wrapper and
   it multiplies the scrim below down with it, and the scrim is the thing keeping
   the headline readable. */
.ab-hero-bg canvas { display: block; width: 100%; height: 100%; opacity: 0.6; }

/* Centre scrim - the one real departure from the recipe, and the reason for it is
   simple: the source is a poster and has nothing on top of it. This one has a
   headline in the middle, and the palette's lightest bar was landing right behind
   it. So the middle is pushed back down and the bars are left to frame the text
   from the sides rather than sit under it. */
.ab-hero-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(74% 64% at 50% 52%,
    rgba(2, 3, 10, 0.88) 0%,
    rgba(2, 3, 10, 0.6) 38%,
    rgba(2, 3, 10, 0) 84%);
}

/* Only what `.u-hero h1` in prospira.css supplies, since that rule is scoped to
   `.u-hero` and this hero is not one - `.u-hero` sets `isolation: isolate`, which
   would trap the glow above the section below it. The size, weight, family and
   gradient all come from the shared `.u-h1-plain` / `.u-h1-serif` spans, so the
   headline is the same object as every other hero's on this site. */
.ab-title {
  margin: 0 0 18px;
  line-height: 1.08;
  letter-spacing: -0.03em;
  color: var(--text-hi);
}
.ab-lead {
  margin: 0 auto;
  max-width: 54ch;
  /* The SAME step as .u-hero-lede in prospira.css, which /utility/ and
     /services/ use. This was clamp(1rem, 2.1vw, 1.12rem) / 1.62 - capping 0.96px
     below the other heroes at every width past the clamp's midpoint, which is
     small enough to look like a mistake rather than a choice, and it was one.
     The 54ch measure stays: this lead is centred and long, so it still wants a
     shorter line than the left-aligned ones. */
  font-size: clamp(1rem, 2.2vw, 1.18rem);
  line-height: 1.6;
  color: var(--text-mid);
}
.ab-lead strong { color: var(--text-hi); font-weight: 500; }
/* The button under the pill row. `.page-about .hero-cta` further down this file
   sets 14px for the closing panel, where the button follows a paragraph; under a
   row of chips that is too tight. Both selectors are two classes deep, so an
   `.ab-hero-in .hero-cta` here simply lost on source order - hence the third
   class. Do not "simplify" it back. */
.page-about .ab-hero-in .hero-cta { margin-top: 24px; }

/* --- the people -----------------------------------------------------------
   Two cards on the anatomy of the team grid the owner picked: ringed portrait,
   name, role in the accent, a row of icon buttons, and a More disclosure. Rebuilt
   on this site's glass and royal rather than the reference's black and red.

   One honest departure. The reference gives each card four social buttons; this
   shop has exactly one external channel - Discord appears 22 times across the
   site and nothing else appears at all - so there is one button, styled the same
   way. Three more would be invented accounts on the page whose entire job is
   being checkable.

   The More disclosure is kept and given something to hold: the line about what
   each person actually does moved inside it. That is what keeps the card the
   compact shape the reference has, instead of a paragraph pushing it tall. Built
   on <details>, so it needs no JavaScript and matches the FAQ elsewhere. */
/* `align-items: start`, so a card is only as tall as its own contents.

   The default stretch tied the two together: opening one card's More grew the
   row, which grew the closed card with it, which slid that card's own More
   button down to the new floor - you expand one and the other one's button
   walks away from you. Nothing is lost by dropping it, because the two cards
   are naturally the same height anyway (measured: 333px each, closed). */
.ab-team {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  gap: 14px;
  max-width: 660px;
  margin: 0 auto;
}
.ab-person {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 28px 22px 22px;
}

/* The reference rings each portrait in its accent. Ours is a royal gradient ring
   with the card's own dark between it and the photo, so the ring reads as a rim
   of light rather than a border drawn on the face. */
.ab-face-ring {
  display: block;
  padding: 3px;
  margin-bottom: 16px;
  border-radius: 999px;
  background: conic-gradient(from 210deg, rgba(42,91,255,0.9), rgba(150,180,255,0.75), rgba(30,58,174,0.85), rgba(42,91,255,0.9));
  box-shadow: 0 10px 30px -18px rgba(42, 91, 255, 0.9);
}
.ab-face {
  display: block;
  width: 96px;
  height: 96px;
  border-radius: 999px;
  object-fit: cover;
  border: 3px solid rgba(9, 13, 28, 0.95);
  background: rgba(150, 180, 255, 0.06);
}
/* line-height set explicitly: without it this inherited the body's 1.6, which on
   a 20px name is a 32px line box. The shared h2 runs 1.12 and the h1 1.08 - a
   heading is tight, and this is a heading wearing a <p>. */
.ab-person-name {
  margin: 0 0 4px;
  font-size: 1.24rem;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.015em;
  color: var(--text-hi);
}
.ab-person-role {
  margin: 0 0 14px;
  font-size: 0.9rem;
  font-weight: 500;
  color: rgba(140, 172, 255, 0.95);
}

/* The reference's icon buttons: a small dark square, glyph centred, lifting on
   hover. 38px keeps it a comfortable target without becoming a call to action -
   the card's job is to name a person, not to route traffic. */
.ab-socials { display: flex; justify-content: center; gap: 8px; margin-bottom: 14px; }
/* 38px of button with a 44px hit area, via an out-of-flow ::after - the same
   trick .announce a uses in prospira.css, and for the same reason: the pill has
   to keep its drawn size while a thumb gets the target it needs. Measured at
   38px before, which is under the 44px minimum on every touch device. */
.ab-social {
  position: relative;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 11px;
  border: 1px solid var(--border-mid);
  background: linear-gradient(180deg, rgba(16, 22, 44, 0.6), rgba(10, 14, 30, 0.68));
  color: var(--text-mid);
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out), transform var(--dur-1) var(--ease-out);
}
.ab-social::after { content: ''; position: absolute; inset: -3px; }
.ab-social:hover { color: #d4e2ff; border-color: rgba(120, 160, 255, 0.42); transform: translateY(-1px); }
.ab-social:focus-visible { outline: 2px solid rgba(94, 140, 255, 0.85); outline-offset: 2px; }

/* The disclosure, shaped like the reference's pill. Native <details>, so the
   keyboard and screen-reader behaviour is the browser's and not reimplemented. */
/* No `margin-top: auto`. It was pinning the button to the card's floor, which is
   half of why opening one card moved the other's - and with the row no longer
   stretching there is no free space for it to consume anyway. The button now
   sits under the socials, where it belongs. */
.ab-more { width: 100%; }
/* 41px drawn, 45px reachable - see .ab-social above for why. */
.ab-more summary {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 15px;
  border-radius: 999px;
  border: 1px solid var(--border-mid);
  background: linear-gradient(180deg, rgba(16, 22, 44, 0.5), rgba(10, 14, 30, 0.58));
  font-size: 0.9rem;
  color: var(--text-mid);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-1) var(--ease-out), border-color var(--dur-1) var(--ease-out);
}
.ab-more summary::after { content: ''; position: absolute; inset: -2px 0; }
.ab-more summary::-webkit-details-marker { display: none; }
.ab-more summary:hover { color: var(--text-hi); border-color: var(--border-strong); }
.ab-more summary:focus-visible { outline: 2px solid rgba(94, 140, 255, 0.85); outline-offset: 2px; }
.ab-more summary svg { transition: transform var(--dur-1) var(--ease-out); }
.ab-more[open] summary svg { transform: rotate(180deg); }
.ab-more p {
  margin: 12px 0 0;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--text-mid);
}
@media (prefers-reduced-motion: reduce) {
  .ab-social, .ab-more summary, .ab-more summary svg { transition: none; }
}

/* --- where we are ----------------------------------------------------------
   A port of the aceternity "World Map" (21st.dev). The original generates its
   dots in the browser with the dotted-map package and draws arcs between city
   pairs; here the dots are a static file built once by make-worldmap.py, and
   there are no arcs. A network of arcs would say "we operate worldwide", which
   is the opposite of true - two people, one city - and this page cannot afford
   even a small lie. One pin, and it is the only bright thing in the picture.

   Held at 0.5 opacity and masked to nothing at all four edges so it reads as a
   backdrop the pin sits on, not a chart the reader is meant to study. The mask
   is the same idiom .bgfx already uses elsewhere on the site. */
.ab-map {
  position: relative;
  width: 100%;
  max-width: 780px;
  margin: 34px auto 0;
  aspect-ratio: 228 / 88;              /* the generated viewBox, so the pin lands true */
}
.ab-map-img {
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0.5;
  -webkit-mask-image:
    linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent),
    linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  mask-image:
    linear-gradient(180deg, transparent, #000 16%, #000 84%, transparent),
    linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

/* A zero-size anchor at the coordinates, with the dot and the label hung off it.
   The label was a sibling of the dot at first, which meant its `left` resolved
   against the map and it sat 400px away from the city it names.

   The percentages come from the same window make-worldmap.py projects the dots
   through, and that script prints them - so they are derived, not eyeballed. Do
   not hand-tune: change the window, re-run the script, paste what it prints. */
.ab-place-mark {
  position: absolute;
  left: 54.7%;
  top: 22.01%;
  width: 0;
  height: 0;
}
.ab-pin {
  position: absolute;
  left: -4.5px;
  top: -4.5px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #dce7ff;
  box-shadow: 0 0 0 2px rgba(42, 91, 255, 0.85), 0 0 14px 2px rgba(42, 91, 255, 0.45);
}
/* The heartbeat. The source animates an SVG circle's r and opacity; a CSS
   keyframe on a pseudo-element does the same thing without the SVG having to
   be inline in the page. */
.ab-pin::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid rgba(120, 160, 255, 0.7);
  animation: ab-ping 2.4s cubic-bezier(0, 0, 0.2, 1) infinite;
}
/* Stops at 3.4, which puts the ring's outer edge at 15.3px on a 9px dot - just
   inside where the label starts. At 4.2 it swept through the first few letters
   of the city name every two seconds, which reads as a rendering fault rather
   than a pulse. */
@keyframes ab-ping {
  0%   { transform: scale(1);   opacity: 0.75; }
  75%  { transform: scale(3.4); opacity: 0; }
  100% { transform: scale(3.4); opacity: 0; }
}
/* The place name rides with the pin, so the map carries the location and the
   facts row below does not have to repeat it. */
.ab-pin-label {
  position: absolute;
  left: 18px;
  top: 0;
  transform: translateY(-50%);
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.01em;
  color: var(--text-hi);
  text-shadow: 0 1px 10px rgba(2, 3, 10, 0.9);
}
@media (prefers-reduced-motion: reduce) {
  .ab-pin::after { animation: none; opacity: 0.5; }
}

/* Facts about the shop, not about either person, so they sit under the pair once. */
.ab-facts {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px 30px;
  margin: 22px auto 0;
}
.ab-fact { display: flex; align-items: baseline; gap: 8px; }
.ab-fact dt {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-low);
}
.ab-fact dd {
  margin: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--text-hi);
}
.ab-fact a { color: #8ea8ff; text-decoration: none; border-bottom: 1px solid transparent; }
.ab-fact a:hover { border-bottom-color: currentColor; }

/* The location is on the map, so it is not also in this row - printing it twice
   is the repetition this page was cut down to avoid. It only appears here when
   the map drops its label, which is narrow screens. Declared after .ab-fact
   because both are one class deep and the later rule wins; putting it earlier
   silently lost, which is how it shipped saying Poznan twice. */
.ab-fact-place { display: none; }

@media (max-width: 560px) {
  .ab-team { grid-template-columns: 1fr; max-width: 330px; }
  .ab-facts { gap: 8px 20px; }
  /* The map stays - it is the nice thing on this page - but the label beside the
     pin would run off the right edge at this width, so the row takes the name
     back and the pin shrinks to stay in proportion with a much smaller map. */
  .ab-map { margin-top: 26px; }
  .ab-pin-label { display: none; }
  .ab-pin { width: 7px; height: 7px; left: -3.5px; top: -3.5px; }
  .ab-fact-place { display: flex; }
}

/* Sized to its lines, not to the container. At full width the card was 1136px
   wide and the longest refusal 512px - 45% used, and the block read as sparse
   rather than deliberate. */
.ab-refuse-card { max-width: 620px; margin-inline: auto; padding: 6px 26px; }

/* --- the refusal list — the signature -------------------------------------
   Every service page lists what it does. This one lists what it will not do, and
   that is the whole idea: the objection behind remote access is not "can they
   help" but "what stops them". A limit you publish is a limit you can be held to.

   Mono, with a boxed cross, and the strike-through moved into the HOVER.

   The strike used to be permanent. It was the better idea on paper and the worse
   one on screen: a 1px rule through 0.92rem monospace lands square on the
   x-height, and every one of these lines got harder to read. That is the wrong
   trade on the one block whose entire job is to be read and believed - and the
   cross was already saying "not this", so the page was negating twice.

   So: legible at rest, struck when you touch it. Run the cursor down the list and
   each promise crosses itself out under your hand. The meaning never depends on
   the hover - the cross and the heading carry it for touch screens and for anyone
   who never points at the list - the strike is the reward for reading it. */
.ab-refuse { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }
.ab-refuse li {
  display: grid;
  grid-template-columns: 24px 1fr;
  align-items: start;
  gap: 12px;
  padding: 13px 4px;
  border-bottom: 1px solid var(--border-soft);
  font-family: var(--font-mono);
  font-size: 0.92rem;
  line-height: 1.5;
  /* A step brighter than the --text-mid it was. With the permanent strike gone
     the line is doing its own work and can afford the contrast. */
  color: rgba(214, 225, 245, 0.86);
}
.ab-refuse li:last-child { border-bottom: 0; }

/* The marker is a box, not a bare glyph. Seven of them make a rail down the left
   edge that the eye can run along, which is what turns a text list into a list
   somebody drew. */
.ab-refuse li::before {
  content: '\00d7';
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-top: -1px;                       /* optical: box vs first line of text */
  border: 1px solid rgba(150, 180, 255, 0.18);
  border-radius: 7px;
  font-size: 0.9rem;
  line-height: 1;
  color: rgba(150, 180, 255, 0.6);
  background: rgba(120, 150, 255, 0.04);
  transition:
    border-color var(--dur-1) var(--ease-out),
    color var(--dur-1) var(--ease-out),
    background-color var(--dur-1) var(--ease-out);
}

/* The strike is a real text-decoration held at transparent, and only its COLOUR
   changes on hover. `text-decoration` itself cannot be animated, but
   `text-decoration-color` can, which is enough.

   The first attempt drew a 1px pseudo-element across the line so it could sweep
   in from the left. Two things killed it. Grid blockifies `display: inline`, so
   the span filled the whole column instead of stopping at the words - measured
   522px of line under 512px of text. And where a refusal wraps to two lines,
   which every one of them does on a phone, a single bar at `top: 50%` lands
   BETWEEN the lines instead of through them. A text-decoration follows every line
   box by definition and cannot get either wrong. */
.ab-refuse li {
  text-decoration: line-through;
  text-decoration-color: transparent;
  text-decoration-thickness: 1px;
  transition: text-decoration-color 0.28s var(--ease-out), color var(--dur-1) var(--ease-out);
}
.ab-refuse li:hover {
  color: var(--text-hi);
  text-decoration-color: rgba(160, 190, 255, 0.6);
}
/* text-decoration propagates into descendants, and the marker is one. */
.ab-refuse li::before { text-decoration: none; }
.ab-refuse li:hover::before {
  border-color: rgba(140, 175, 255, 0.42);
  color: rgba(190, 212, 255, 0.9);
  background: rgba(120, 150, 255, 0.1);
}
@media (prefers-reduced-motion: reduce) {
  .ab-refuse li, .ab-refuse li::before { transition: none; }
}
/* These wrap to two lines on a phone and that is fine. Fitting the longest
   refusal on one line inside 299px would need an 8.6px font, so the choice was
   never "one line or two" - it was "two lines, or unreadable". The grid puts the
   cross in its own column, so a wrapped line hangs under the first rather than
   sliding under the marker, and the pair still reads as one item. Size is held at
   something you can actually read. */
@media (max-width: 560px) {
  .ab-refuse-card { padding: 4px 16px; }
  .ab-refuse li {
    grid-template-columns: 18px 1fr;
    gap: 9px;
    padding: 11px 2px;
    font-size: 0.86rem;
  }
}

/* --- the promise, and its limit -------------------------------------------
   Two facing halves, because they are two halves of one honest answer: there is
   no refund, and here is what you get instead. Splitting them stops the page
   sounding like it is hiding the first one.

   Laid out on the FUI bento card (21st.dev): a tall artwork panel with the copy
   riding up over its lower edge on a blur, so the picture reads through the text
   block instead of stopping above it. Ported to plain CSS - the source is React
   with framer-motion and clsx, and the hover it animates is already on this site
   as `.card`'s cursor spotlight, so the motion wrapper had nothing to add.

   THE ARTWORK PANELS ARE EMPTY ON PURPOSE. Two SVGs are still to be drawn. The
   panel is not blank-blank - it carries a faint royal wash so the card reads as
   composed rather than broken - but there is nothing in it yet. */
.ab-deal { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
/* Capped at the same 620px the refusal card uses, so the page has ONE
   single-column measure. Without the cap a card here ran the full container -
   697px at 768, which put its paragraph at 90 characters a line. Comfortable
   prose is 45-75; 90 is where the eye starts losing its place returning to the
   left margin. */
@media (max-width: 820px) {
  .ab-deal { grid-template-columns: 1fr; max-width: 620px; margin-inline: auto; }
}
.ab-deal-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* The source's inset top-glow, `0 -20px 80px -20px #8686f01f`, recoloured off
     its violet onto royal. Layered over .card's own shadow, not replacing it. */
  box-shadow: var(--card-shadow), 0 -20px 80px -20px rgba(86, 116, 255, 0.14) inset;
}

/* The artwork. Cropped on every side by `slice`, which is what stops it reading
   as an illustration parked in a box - the same reason the reference crops its
   render at the card edge. */
.ab-deal-art {
  position: relative;
  flex-shrink: 0;
  height: clamp(196px, 23vw, 278px);
}
.ab-deal-svg { display: block; width: 100%; height: 100%; }
/* The axis labels. Styled here rather than as SVG attributes so they inherit the
   site's mono face - the same one the eyebrows, the facts row and the refusal
   list are set in, which is what makes the artwork read as part of the page
   instead of an illustration dropped into it. */
.ab-deal-axis text {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.08em;
}

/* Pulled up over the artwork on a blur - the whole point of this layout.

   NO top border. One shipped here first and it was the single thing that made
   the card read as two stacked rectangles instead of a picture with a caption.

   NO backdrop-filter either, and that is the more interesting one. The reference
   frosts this panel, which suits it: the thing behind its glass is a big soft 3D
   render, and blurring a soft render just makes it softer. Behind ours is a
   column field - structure - and a 22px blur averages thin bars and their gaps
   into a flat wash, so the picture stopped dead at the panel's top edge. Worse,
   backdrop-filter paints a hard rectangular boundary where it begins, which is
   the "hard" edge you can see the moment it engages. The page looked better
   during load, before the filter kicked in, than after - which is the whole
   argument against it.

   So the panel is a plain translucent ramp over the artwork. 0.14 at the top,
   where the columns should read straight through, to 0.90 at the last line,
   where the paragraph needs the contrast. */
/* Grows to fill whatever height the grid gives the card, and hangs its copy off
   the BOTTOM. Without this the text sits at the top of the panel and the taller
   of the two cards leaves the shorter one with a strip of dead space under its
   last line - the copy ends up floating in the middle of the card instead of
   sitting on its floor, which is where the reference puts it. */
.ab-deal-body {
  position: relative;
  z-index: 2;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  margin-top: -100px;
  padding: 26px 24px 24px;
  background: linear-gradient(180deg,
    rgba(4, 7, 18, 0.14) 0%,
    rgba(4, 7, 18, 0.58) 38%,
    rgba(4, 7, 18, 0.90) 100%);
}
/* Brighter than the --text-low every other eyebrow on this page uses, because
   this one is the only one printed over a picture rather than over the page.
   At --text-low it measured 3.6:1 against the lit columns behind it - under AA.
   This clears 5:1. */
.ab-deal-k {
  margin: 0 0 9px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(192, 208, 242, 0.74);
}
/* Bigger than the 1.08rem it was. On the bento the title is the card's anchor and
   has to out-weigh a description sitting right under it. */
.ab-deal-h { margin: 0 0 10px; font-size: clamp(1.16rem, 1.5vw, 1.32rem); font-weight: 600; line-height: 1.25; letter-spacing: -0.014em; color: var(--text-hi); }
.ab-deal-p { margin: 0; font-size: 0.9rem; line-height: 1.62; color: var(--text-mid); }
.ab-deal-p + .ab-deal-p { margin-top: 10px; }

@media (max-width: 560px) {
  .ab-deal-art { height: clamp(158px, 42vw, 200px); }
  .ab-deal-body { margin-top: -76px; padding: 22px 20px 20px; }
}

@media (prefers-reduced-motion: reduce) {
  .ab-tile, .ab-tile::after { transition: none; }
}

/* --- the closing panel ------------------------------------------------------
   The markup is the home page's closing card, so the two pages finish on the same
   note. Its styling is not, because landing.css declares itself "scoped to
   body.page-home" in its own first line: .cta-panel, .cta-glow and .hero-cta are
   all written as .page-home descendants and do nothing here. Loading the file was
   not enough, which is why the panel first shipped flush left with no padding.

   The values below are copied from landing.css rather than moved out of it. The
   codebase's own convention is move-and-rescope (see the note at landing.css:31),
   and that is the tidier answer - but it means editing the stylesheet of the
   busiest page on the site to serve one new page. Copying costs a little drift
   risk; moving costs a regression on the home page's closing CTA. If that panel
   is ever restyled, this block has to follow it.

   .has-bgfx and .bgfx are NOT duplicated: those already live in prospira.css and
   say "page-agnostic" on the tin. */
.page-about .cta-panel {
  --stagger-lead: 90ms;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  text-align: center;
  padding: clamp(44px, 7vw, 84px) clamp(22px, 5vw, 60px);
  border-radius: 26px;
  overflow: hidden;
}
.page-about .cta-panel > * { position: relative; }
.page-about .cta-panel h2 {
  font-size: clamp(1.8rem, 4vw, 2.7rem);
  font-weight: 600;
  letter-spacing: -0.032em;
  line-height: 1.1;
  color: var(--text-hi);
}
/* The one place on this page where the serif is a gradient rather than flat: it
   is the closing line, and it matches the home page's word for word treatment. */
.page-about .cta-panel h2 em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: -0.012em;
  background: linear-gradient(115deg, #d4e2ff 8%, #7fa2ff 54%, var(--accent) 100%);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
}
.page-about .cta-glow {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60% 90% at 50% 115%, rgba(42, 91, 255, 0.28), transparent 65%),
    radial-gradient(40% 60% at 15% -10%, rgba(190, 210, 255, 0.08), transparent 60%);
}
.page-about .hero-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 13px;
  margin-top: 14px;
}
@media (max-width: 560px) {
  .page-about .hero-cta .btn,
  .page-about .hero-cta .shiny-cta { width: 100%; }
}


/* ============================ SHINY CTA (lifted from landing.css) ============
   This page loaded css/landing.css - 51KB raw, 14KB over the wire - for exactly
   ONE thing: this button, in the closing panel. Measured before the move: of the
   193 rules in that stylesheet, 4 matched anything on this page, and the only
   reason disabling it changed every element was the 16 --sh-* custom properties
   it puts on :root - all of which serve this button too.

   So the block is copied here verbatim and the <link> is gone.

   IF THE HOME PAGE'S .shiny-cta IS EVER RESTYLED, THIS COPY HAS TO FOLLOW IT -
   the same standing warning the closing panel above already carries. Copying is
   the deliberate choice, as it was there: moving the rules into prospira.css
   would edit the stylesheet of every page on the site to serve one button on
   this one.

   @property and @keyframes are document-scoped, not selector-scoped, so they
   come along or the animation silently does nothing. */
/* ============================ SHINY CTA ============================
   Vanilla port of 21st.dev ShinyButton: a conic-gradient border rotating around
   the pill + a faint dot texture + an inner shimmer, recolored royal. @property
   drives the animated angle; browsers without it just show a static border
   (button still fully works). */
@property --sh-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@property --sh-angle-offset { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@property --sh-percent { syntax: '<percentage>'; initial-value: 5%; inherits: false; }
@property --sh-shine { syntax: '<color>'; initial-value: #eaf0ff; inherits: false; }
.shiny-cta {
  --sh-bg: #070b16;
  --sh-bg-subtle: #141a2e;
  --sh-fg: #eef3ff;
  --sh-highlight: #2a5bff;
  --sh-highlight-subtle: #7fa2ff;
  --sh-anim: sh-angle linear infinite;
  --sh-dur: 3s;
  --sh-shadow: 2px;
  --sh-trans: 800ms cubic-bezier(0.25, 1, 0.5, 1);
  isolation: isolate;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  cursor: pointer;
  outline-offset: 4px;
  padding: 0.92rem 2.1rem;
  font-family: var(--font-sans);
  font-size: 0.98rem;
  line-height: 1.2;
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  border: 1px solid transparent;
  border-radius: 360px;
  color: var(--sh-fg);
  background:
    linear-gradient(var(--sh-bg), var(--sh-bg)) padding-box,
    conic-gradient(from calc(var(--sh-angle) - var(--sh-angle-offset)),
      transparent,
      var(--sh-highlight) var(--sh-percent),
      var(--sh-shine) calc(var(--sh-percent) * 2),
      var(--sh-highlight) calc(var(--sh-percent) * 3),
      transparent calc(var(--sh-percent) * 4)) border-box;
  box-shadow: inset 0 0 0 1px var(--sh-bg-subtle);
  transition: var(--sh-trans);
  transition-property: --sh-angle-offset, --sh-percent, --sh-shine;
}
.shiny-cta::before,
.shiny-cta::after,
.shiny-cta span::before {
  content: '';
  pointer-events: none;
  position: absolute;
  inset-inline-start: 50%;
  inset-block-start: 50%;
  translate: -50% -50%;
  z-index: -1;
}
.shiny-cta:active { translate: 0 1px; }
.shiny-cta::before {          /* dot texture */
  --size: calc(100% - var(--sh-shadow) * 3);
  --position: 2px;
  --space: calc(var(--position) * 2);
  width: var(--size);
  height: var(--size);
  background: radial-gradient(circle at var(--position) var(--position),
    #cfe0ff calc(var(--position) / 4), transparent 0) padding-box;
  background-size: var(--space) var(--space);
  background-repeat: space;
  -webkit-mask-image: conic-gradient(from calc(var(--sh-angle) + 45deg), black, transparent 10% 90%, black);
  mask-image: conic-gradient(from calc(var(--sh-angle) + 45deg), black, transparent 10% 90%, black);
  border-radius: inherit;
  opacity: 0.4;
  z-index: -1;
}
.shiny-cta::after {           /* inner shimmer (the glow that sweeps through the pill) */
  --sh-anim: sh-shimmer linear infinite;
  width: 100%;
  aspect-ratio: 1;
  background: linear-gradient(-50deg, transparent, var(--sh-highlight), transparent);
  -webkit-mask-image: radial-gradient(circle at bottom, transparent 40%, black);
  mask-image: radial-gradient(circle at bottom, transparent 40%, black);
  opacity: 0.45;
}
.shiny-cta span { z-index: 1; position: relative; display: inline-flex; align-items: center; gap: 8px; }
.shiny-cta span svg { transition: transform var(--dur-1) var(--ease-out); }
.shiny-cta:is(:hover, :focus-visible) span svg { transform: translateX(3px); }
.shiny-cta span::before {     /* breathing hover glow - removed (grew as a square) */
  display: none;
  --size: calc(100% + 1rem);
  width: var(--size);
  height: var(--size);
  box-shadow: inset 0 -1ex 2rem 4px var(--sh-highlight);
  opacity: 0;
  transition: opacity var(--sh-trans);
  animation: calc(var(--sh-dur) * 1.5) sh-breathe linear infinite;
}
.shiny-cta,
.shiny-cta::before,
.shiny-cta::after {
  animation: var(--sh-anim) var(--sh-dur),
    var(--sh-anim) calc(var(--sh-dur) / 0.4) reverse paused;
  animation-composition: add;
}
.shiny-cta:is(:hover, :focus-visible) {
  --sh-percent: 20%;
  --sh-angle-offset: 95deg;
  --sh-shine: var(--sh-highlight-subtle);
}
.shiny-cta:is(:hover, :focus-visible),
.shiny-cta:is(:hover, :focus-visible)::before,
.shiny-cta:is(:hover, :focus-visible)::after { animation-play-state: running; }
.shiny-cta:is(:hover, :focus-visible) span::before { opacity: 1; }
@keyframes sh-angle { to { --sh-angle: 360deg; } }
@keyframes sh-shimmer { to { rotate: 360deg; } }
@keyframes sh-breathe { from, to { scale: 1; } 50% { scale: 1.2; } }
@media (prefers-reduced-motion: reduce) {
  .shiny-cta, .shiny-cta::before, .shiny-cta::after, .shiny-cta span::before { animation: none !important; }
}
