/* amels.com — Dr Migrate design language (tokens per UI Guidelines §2).
   Design system only: no Dr Migrate logo, wordmark or product lockup appears
   on this site, which is a personal professional page and must not read as a
   product surface. */

/* Subset to the ~139 characters this site actually renders, then woff2.
   470KB of full TTFs -> 17.6KB. Regenerate with: python tools/subset_fonts.py
   The source .ttf files stay in the repo but are no longer served. */
@font-face { font-family:'Poppins'; font-weight:400; font-style:normal; font-display:swap;
             src:url('/fonts/Poppins-Regular.woff2')  format('woff2'); }
@font-face { font-family:'Poppins'; font-weight:500; font-style:normal; font-display:swap;
             src:url('/fonts/Poppins-Medium.woff2')   format('woff2'); }
@font-face { font-family:'Poppins'; font-weight:600; font-style:normal; font-display:swap;
             src:url('/fonts/Poppins-SemiBold.woff2') format('woff2'); }

:root {
  /* Brand ramp */
  --drm-purple:    #6765F2;
  --drm-purple-d:  #4f4ddb;
  --drm-blue:      #5F74EB;

  /* Text */
  --drm-ink:       #111827;
  --drm-body:      #374151;
  --drm-muted:     #6b7280;

  /* Surfaces & lines */
  --drm-border:      #d1d5db;
  --drm-card-border: #eceef4;
  --drm-bg:          #f6f7fb;
  --drm-bg-accent:   #eef0fb;

  /* Status */
  --drm-ok:   #16a34a;
  --drm-err:  #b41d14;
  --drm-warn: #e8720c;

  /* Composed */
  --drm-gradient:    linear-gradient(135deg, var(--drm-purple) 0%, var(--drm-blue) 100%);
  --drm-shadow-card: 0 18px 48px rgba(76, 66, 170, .10);
  --drm-shadow-soft: 0 8px 22px rgba(103, 101, 242, .12);
  --drm-radius:      16px;

  --mono: "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
}

*{ box-sizing:border-box }

body{
  margin:0;
  background:var(--drm-bg);
  color:var(--drm-body);
  font-family:'Poppins', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  font-size:16px; line-height:1.6;
  -webkit-font-smoothing:antialiased;
}

a{ color:var(--drm-blue); text-decoration:none }
a:hover{ color:var(--drm-purple-d) }
a:focus-visible{ outline:none; border-radius:6px;
                 box-shadow:0 0 0 3px rgba(95,116,235,.14) }

.wrap{ max-width:68rem; margin:0 auto; padding:0 24px }
.narrow{ max-width:52rem }

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

.hero{
  position:relative; overflow:hidden;
  background:linear-gradient(160deg, #F4F3FD, #F1F0FD 55%, #ECEAFB);
  border-bottom:1px solid var(--drm-card-border);
  padding:76px 0 68px;
}
.hero::before{                    /* dotted field, corner-masked (§6) */
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:radial-gradient(rgba(103,101,242,.10) 1.3px, transparent 1.3px);
  background-size:24px 24px;
  -webkit-mask-image:radial-gradient(120% 90% at 88% 8%, #000 0%, transparent 62%);
          mask-image:radial-gradient(120% 90% at 88% 8%, #000 0%, transparent 62%);
}
.hero::after{                     /* soft glow blob (§6) */
  content:''; position:absolute; width:640px; height:640px; right:-220px; top:-300px;
  border-radius:50%; pointer-events:none;
  background:radial-gradient(circle, rgba(103,101,242,.10) 0%, transparent 70%);
}
.hero .wrap{ position:relative }

.eyebrow{
  font-size:11px; font-weight:600; color:var(--drm-muted);
  text-transform:uppercase; letter-spacing:.06em; margin:0 0 14px;
}
h1{
  font-size:clamp(38px, 3.6vw, 56px); font-weight:600; color:var(--drm-ink);
  letter-spacing:-.015em; line-height:1.12; margin:0 0 6px;
}
.role{
  font-size:17px; font-weight:500; color:var(--drm-body); margin:0 0 22px;
  max-width:44rem;
}
.role .grad{
  background:var(--drm-gradient); -webkit-background-clip:text; background-clip:text;
  color:transparent; font-weight:600;
}
.lede{
  font-size:19px; line-height:1.5; color:var(--drm-ink); font-weight:500;
  max-width:40rem; margin:0 0 26px;
}

.meta{ display:flex; flex-wrap:wrap; gap:10px; align-items:center; margin:0 0 28px }
.chip{
  display:inline-flex; align-items:center; gap:7px;
  background:#fff; border:1px solid var(--drm-card-border);
  border-radius:999px; padding:6px 14px;
  font-size:13px; font-weight:500; color:var(--drm-body);
  box-shadow:0 4px 14px rgba(76,66,170,.06);
}
.chip .dot{ width:7px; height:7px; border-radius:50%; background:var(--drm-gradient) }

.actions{ display:flex; flex-wrap:wrap; gap:12px }
.btn-primary{
  display:inline-block; background:var(--drm-gradient); color:#fff; border:none;
  border-radius:12px; padding:12px 28px; font-weight:600; font-size:15px;
  font-family:inherit; cursor:pointer;
  box-shadow:0 10px 22px -8px rgba(95,116,235,.6);
  transition:box-shadow .15s ease, transform .05s ease;
}
.btn-primary:hover{ color:#fff; box-shadow:0 14px 26px -8px rgba(95,116,235,.72) }
.btn-primary:active{ transform:translateY(1px) }
.btn-ghost{
  display:inline-block; background:#fff; color:var(--drm-ink);
  border:1.5px solid var(--drm-purple); border-radius:10px;
  padding:10px 18px; font-weight:600; font-size:15px;
  transition:box-shadow .15s ease, background .15s ease, border-color .15s ease;
}
.btn-ghost:hover{ color:var(--drm-ink); border-color:var(--drm-purple-d);
                  background:#faf9ff; box-shadow:var(--drm-shadow-soft) }

/* ---------- sections ---------- */

section{ padding:66px 0 }
section.tight{ padding:48px 0 }
.section-tag{
  font-size:11px; font-weight:600; color:var(--drm-muted);
  text-transform:uppercase; letter-spacing:.06em; margin:0 0 10px;
}
h2{ font-size:30px; font-weight:600; color:var(--drm-ink);
    letter-spacing:-.01em; line-height:1.2; margin:0 0 12px }
.section-intro{ font-size:17px; max-width:44rem; margin:0 0 32px }
h3{ font-size:17px; font-weight:600; color:var(--drm-ink); margin:0 0 8px }
p{ margin:0 0 14px }
p:last-child{ margin-bottom:0 }
strong{ color:var(--drm-ink); font-weight:600 }
code{ font-family:var(--mono); font-size:.86em; color:var(--drm-purple-d);
      background:var(--drm-bg-accent); padding:.1em .38em; border-radius:6px }

.grid{ display:grid; gap:20px; grid-template-columns:repeat(auto-fit, minmax(288px, 1fr)) }

.card{
  background:#fff; border:1px solid var(--drm-card-border);
  border-radius:var(--drm-radius); padding:24px;
  box-shadow:var(--drm-shadow-card);
}
.card p{ font-size:14.5px }
.card ul{ margin:10px 0 0; padding-left:18px; font-size:14px }
.card li{ margin:0 0 6px }
.card li::marker{ color:var(--drm-purple) }

.icon-chip{
  width:48px; height:48px; border-radius:14px; background:#fff;
  display:grid; place-items:center; margin:0 0 16px;
  box-shadow:0 8px 22px rgba(76,66,170,.14);
}
.icon-chip svg{ width:22px; height:22px; stroke:var(--drm-purple);
                fill:none; stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round }

.hint{
  padding:14px 18px; border-radius:12px;
  background:var(--drm-bg-accent); border:1px solid #dfe2f7;
  border-left:3px solid var(--drm-purple);
  font-size:14.5px;
}
.notice--info{
  display:flex; gap:12px; padding:14px 16px; border-radius:14px;
  background:#eef2fd; border:1px solid #c9d4f7; font-size:14.5px;
}

/* closing statement — affirmative, not a caveat box */
.closing{
  position:relative; display:flex; gap:12px;
  padding:26px 30px 26px 32px; border-radius:var(--drm-radius);
  background:#fff; border:1px solid var(--drm-card-border);
  box-shadow:var(--drm-shadow-card); overflow:hidden;
}
.closing::before{
  content:''; position:absolute; left:0; top:0; bottom:0; width:4px;
  background:var(--drm-gradient);
}
.closing p{ font-size:15.5px; line-height:1.62; margin:0 0 14px }
.closing p:last-child{
  margin-bottom:0; color:var(--drm-ink); font-weight:500; font-size:16.5px;
}

.status{ display:inline-block; border-radius:999px; padding:3px 12px;
         font-size:12px; font-weight:600 }
.status.running{ background:var(--drm-bg-accent); color:var(--drm-purple-d) }
.status.done{ background:#e9f7ef; color:var(--drm-ok) }

/* principles list */
.principles{ display:grid; gap:14px }
.principle{
  background:#fff; border:1px solid var(--drm-card-border); border-radius:14px;
  padding:18px 22px; box-shadow:0 6px 18px rgba(76,66,170,.05);
}
.principle h3{ margin:0 0 4px }
.principle p{ font-size:14.5px; color:var(--drm-body) }

/* career arc — narrative, deliberately undated */
.arc{ position:relative; padding-left:34px }
.arc::before{
  content:''; position:absolute; left:7px; top:10px; bottom:10px; width:2px;
  background:linear-gradient(180deg, var(--drm-purple), var(--drm-blue));
  opacity:.35; border-radius:2px;
}
.arc-item{ position:relative; padding:0 0 30px }
.arc-item:last-child{ padding-bottom:0 }
.arc-item::before{
  content:''; position:absolute; left:-34px; top:7px; width:16px; height:16px;
  border-radius:50%; background:var(--drm-gradient);
  box-shadow:0 0 0 4px var(--drm-bg), 0 6px 14px rgba(103,101,242,.28);
}
.arc-item h3{ font-size:18px; margin:0 0 6px }
.arc-item p{ font-size:15px }
.arc-item .then{
  font-size:11px; font-weight:600; color:var(--drm-muted);
  text-transform:uppercase; letter-spacing:.06em; display:block; margin:0 0 3px;
}

/* range — breadth without a timeline */
.range{ display:grid; gap:20px; grid-template-columns:repeat(auto-fit, minmax(250px, 1fr)) }
.range .card h3{ font-size:14px; text-transform:uppercase; letter-spacing:.05em;
                 color:var(--drm-muted); font-weight:600; margin:0 0 12px }
.range ul{ margin:0; padding:0; list-style:none; font-size:14.5px }
.range li{ margin:0 0 7px; padding-left:16px; position:relative; color:var(--drm-body) }
.range li::before{ content:''; position:absolute; left:0; top:9px; width:5px; height:5px;
                   border-radius:50%; background:var(--drm-purple); opacity:.55 }

.tags{ display:flex; flex-wrap:wrap; gap:8px; margin-top:6px }
.tag{ font-size:12.5px; font-weight:500; color:var(--drm-purple-d);
      background:var(--drm-bg-accent); border-radius:999px; padding:3px 11px }

footer{
  background:#fff; border-top:1px solid var(--drm-card-border);
  padding:44px 0 40px; margin-top:20px;
}
footer .cols{ display:flex; flex-wrap:wrap; gap:28px; justify-content:space-between;
              align-items:flex-end }
footer p{ font-size:14.5px }
footer .fine{ font-size:12.5px; color:var(--drm-muted); margin-top:22px }

@media (max-width:640px){
  section{ padding:48px 0 }
  .row{ grid-template-columns:1fr; gap:6px }
  .row .when{ padding-top:0 }
}

/* ================================================================
   Motion
   ================================================================
   Ported from the countdown page, minus everything that only worked
   because that page had one job. What came over: a drifting glow, a
   gradient that moves, reveals tied to scroll position. What did not:
   colour that escalates, anything that pulses, and the whole idea of
   the page building toward something. This page is read, not watched.

   All of it is pure CSS. The site-wide CSP is script-src 'none' and
   that is worth more than any scroll effect, so nothing here is
   allowed to need JavaScript.

   Three guards below are load-bearing, not politeness:

     @supports (animation-timeline: view())   — scroll-driven animation
       is not everywhere yet. Reveals start at opacity:0, so without
       this guard a browser that cannot run them renders a blank page
       and reports no error at all. Never move a reveal rule out of it.

     prefers-reduced-motion: no-preference    — opt in, never opt out.
       Same reason: the un-animated state has to be the visible one.

     @media print                             — a print context has no
       scrollport for view() to resolve against. Belt and braces so a
       PDF of this page is never a stack of empty cards.
   ---------------------------------------------------------------- */

/* ---------- 1. the hero glow drifts ---------- */
/* The blob was already there and already static. Transform on a pseudo-
   element stays on the compositor, so this costs no layout and no paint. */
@media (prefers-reduced-motion: no-preference){
  .hero::after{ animation:hero-drift 38s ease-in-out infinite }
}
@keyframes hero-drift{
  50%{ transform:translate(-70px, 46px) scale(1.14) }
}

/* ---------- 2. the gradient in the role line moves ---------- */
/* Widening the gradient box and sliding it is what makes this work; the
   text is a background-clip:text mask and does not move at all.
   Do not add a filter to this element — Chromium composites a filtered
   box before it applies background-clip:text and the gradient comes out
   flat grey. That bug cost an hour on the countdown page. */
.role .grad{ background-size:230% 100%; background-position:0 0 }
@media (prefers-reduced-motion: no-preference){
  .role .grad{ animation:grad-slide 16s ease-in-out infinite }
}
@keyframes grad-slide{ 50%{ background-position:100% 0 } }

/* ---------- 3. the hero arrives ---------- */
/* Short, once, on load. Staggered by a few tens of milliseconds so the
   eye lands on the name first. */
@media (prefers-reduced-motion: no-preference){
  .hero .eyebrow,
  .hero h1,
  .hero .role,
  .hero .lede,
  .hero .meta,
  .hero .actions{ animation:rise-in .62s cubic-bezier(.16,1,.3,1) both }
  .hero h1      { animation-delay:.05s }
  .hero .role   { animation-delay:.11s }
  .hero .lede   { animation-delay:.17s }
  .hero .meta   { animation-delay:.23s }
  .hero .actions{ animation-delay:.29s }
}
@keyframes rise-in{
  from{ opacity:0; transform:translateY(14px) }
  to  { opacity:1; transform:none }
}

/* ---------- 4. content rises as it scrolls in ---------- */
/* This is the effect worth having, and the one that can take the site
   down. Read the guard note at the top of this block before editing. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .card,
    .principle,
    .arc-item,
    .closing,
    .hint,
    .section-tag,
    section > .wrap > h2{
      animation:rise-in-scroll linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 24%;
    }
  }
}
@keyframes rise-in-scroll{
  from{ opacity:0; transform:translateY(20px) }
  to  { opacity:1; transform:none }
}

/* ---------- 5. the career arc draws itself ---------- */
/* The line already ran the full height of the section as a static
   gradient. Tying its scaleY to scroll position turns it into a
   progress indicator for the story it sits beside — the one place on
   this page where motion carries meaning rather than decoration. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .arc::before{
      transform-origin:top center;
      animation:draw-line linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 72%;
    }
  }
}
@keyframes draw-line{ from{ transform:scaleY(0) } to{ transform:scaleY(1) } }

/* ---------- 6. cards answer the pointer ---------- */
/* They had no hover state at all. hover:hover keeps this off touch
   screens, where :hover sticks after a tap and reads as a stuck card. */
@media (hover:hover){
  .card,
  .principle{ transition:transform .22s ease, box-shadow .22s ease }
  .card:hover{ transform:translateY(-3px);
               box-shadow:0 26px 58px rgba(76,66,170,.16) }
  .principle:hover{ transform:translateY(-2px);
                    box-shadow:0 14px 34px rgba(76,66,170,.10) }
  .icon-chip{ transition:transform .28s cubic-bezier(.22,1,.3,1) }
  .card:hover .icon-chip{ transform:translateY(-2px) rotate(-4deg) }
}

/* ---------- 7. navigating between pages cross-fades ---------- */
/* One at-rule buys a real transition between / and /specification, with no
   script and no framework. A browser that does not know @view-transition
   ignores the block and navigates the old way, so there is nothing to guard
   against — only reduced motion, which gets an instant swap instead. */
@view-transition{ navigation:auto }
@media (prefers-reduced-motion: reduce){
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*){ animation:none !important }
}

/* ---------- 8. a reading-progress rule ---------- */
/* scroll(root) rather than view(): this one tracks the document, not an
   element. It earns its place on /specification, which is long enough that
   knowing how much is left changes whether people keep going.

   Safe by construction — the un-animated state is scaleX(0), which is an
   invisible bar. A timeline that never resolves shows nothing rather than
   showing something wrong, so this one needs no escape hatch beyond print. */
@supports (animation-timeline: scroll()){
  @media (prefers-reduced-motion: no-preference){
    body::before{
      content:''; position:fixed; inset:0 0 auto 0; height:3px; z-index:99;
      background:var(--drm-gradient);
      transform:scaleX(0); transform-origin:0 50%;
      animation:read-progress linear both;
      animation-timeline:scroll(root);
    }
  }
}
@keyframes read-progress{ to{ transform:scaleX(1) } }

/* ---------- 9. the hero hands off ---------- */
/* Drifts up and dissolves as it leaves rather than sliding away flat. Only a
   `to` keyframe, so `from` is whatever the element already computes to — the
   backwards fill before the range starts is therefore the untouched, fully
   visible hero. That is what makes this safe without a @supports fallback
   for the start state. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .hero .wrap{
      animation:hero-handoff linear both;
      animation-timeline:view();
      animation-range:exit 0% exit 100%;
    }
  }
}
@keyframes hero-handoff{ to{ opacity:0; transform:translateY(-40px) } }

/* ---------- 10. cards in a row cascade ---------- */
/* They previously arrived together, because grid siblings share a scroll
   position and therefore share a timeline. Stretching the range further for
   each later sibling makes them finish in sequence. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .grid > .card:nth-child(2),  .range > .card:nth-child(2),
    .principles > .principle:nth-child(2){ animation-range:entry 4% cover 30% }
    .grid > .card:nth-child(3),  .range > .card:nth-child(3),
    .principles > .principle:nth-child(3){ animation-range:entry 4% cover 36% }
    .grid > .card:nth-child(4),  .range > .card:nth-child(4),
    .principles > .principle:nth-child(4){ animation-range:entry 4% cover 42% }
    .principles > .principle:nth-child(5){ animation-range:entry 4% cover 48% }
  }
}

/* ---------- 11. the card icons draw themselves ---------- */
/* dasharray 240 is chosen to exceed the longest path in any of these icons —
   the arc in the technical-debt glyph is the longest at well under that. Set
   it too low and a path renders permanently dashed instead of solid.

   The dash properties live inside the guard together with the animation, so a
   reduced-motion reader never receives the hidden start state at all. Print is
   the exception and is handled below: it does not change reduced-motion, so
   without an explicit reset a printed page has five blank icon chips. */
@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){
    .icon-chip svg{
      stroke-dasharray:240; stroke-dashoffset:240;
      animation:draw-icon linear both;
      animation-timeline:view();
      animation-range:entry 4% cover 34%;
    }
  }
}
@keyframes draw-icon{ to{ stroke-dashoffset:0 } }

/* ---------- 12. quiet polish ---------- */
/* Not motion. text-wrap:balance stops short headings breaking one word onto
   their own line; pretty does the same for the last line of a paragraph. Both
   degrade to normal wrapping wherever they are unknown. */
h1,h2,h3{ text-wrap:balance }
.lede,.section-intro,.card p,.principle p{ text-wrap:pretty }
::selection{ background:rgba(103,101,242,.20); color:var(--drm-ink) }

@media (hover:hover){
  .btn-primary{ position:relative; overflow:hidden; isolation:isolate }
  .btn-primary::after{
    content:''; position:absolute; inset:0; z-index:-1;
    background:linear-gradient(105deg, transparent 36%,
                rgba(255,255,255,.34) 50%, transparent 64%);
    transform:translateX(-130%);
    transition:transform .6s cubic-bezier(.3,0,.2,1);
  }
  .btn-primary:hover::after{ transform:translateX(130%) }
}

/* ---------- the escape hatches ---------- */
/* Scoped to the elements this block animates rather than to *. A blanket
   opacity:1 !important also overrides the two opacities the design uses
   on purpose — the .35 on the career-arc line and the .55 on the range
   bullets — and silently flattens both. Add a selector here whenever you
   add one above; forgetting is how a card ends up invisible in print. */
@media print, (prefers-reduced-motion: reduce){
  .hero .eyebrow, .hero h1, .hero .role, .hero .lede, .hero .meta, .hero .actions,
  .card, .principle, .arc-item, .closing, .hint, .section-tag,
  section > .wrap > h2{
    animation:none !important;
    opacity:1 !important;
    transform:none !important;
  }
  .hero::after,
  .role .grad{ animation:none !important }

  /* transform only — this one keeps its opacity:.35 */
  .arc::before{ animation:none !important; transform:none !important }

  .hero .wrap{ animation:none !important; opacity:1 !important; transform:none !important }

  /* Without this the icons print as five empty chips: print does not change
     prefers-reduced-motion, so the dashed start state survives into a PDF
     while the timeline that would resolve it does not exist. */
  .icon-chip svg{ stroke-dasharray:none !important; stroke-dashoffset:0 !important;
                  animation:none !important }

  body::before{ display:none !important }

  .card, .principle, .icon-chip, .btn-primary::after{ transition:none !important }
}

/* ================================================================
   Presence
   ================================================================
   Motion was never what was holding this page back. Every section sat
   on one background, every card got one treatment, and the type scale
   was flat enough that nothing had weight — h2 at 30px against 16px
   body reads as a slightly larger paragraph, not as a heading.

   This block is structure and surface. Still no JavaScript, and no
   change to a single word of copy: everything below targets what is
   already in the markup.
   ---------------------------------------------------------------- */

/* ---------- 13. a type scale with actual contrast ---------- */
h2{
  font-size:clamp(30px, 3.1vw, 40px);
  letter-spacing:-.022em; line-height:1.14;
  margin:0 0 14px;
}
.section-tag{
  font-size:11px; letter-spacing:.14em;
  color:var(--drm-purple-d); opacity:.85;
}
.section-intro{ font-size:18px; line-height:1.62; color:var(--drm-body) }
h3{ font-size:17.5px; letter-spacing:-.008em }
section{ padding:88px 0 }
section.tight{ padding:64px 0 }

/* ---------- 14. section rhythm ---------- */
/* Alternating the surface is what turns a long scroll into chapters.
   Hairlines rather than hard edges, so the change reads as a shift in
   material instead of as a seam.

   :not(.finale) is load-bearing. nth-of-type is more specific than a
   class, so on any even section count the white would win and the dark
   ending would silently disappear — which is exactly what happened the
   first time a section was removed. */
main > section:nth-of-type(even):not(.finale){
  background:#fff;
  border-block:1px solid var(--drm-card-border);
}

/* ---------- 15. the page finishes dark ---------- */
/* The closing section is the argument's last word, and on a page this
   long an unbroken light field gives it nothing to land against.
   Driven by an explicit .finale class rather than by :last-of-type,
   which would hand the page's ending to whatever section happened to
   be last. specification.html has no <main> and no .finale, so its own
   closing section — the candid limits list — keeps its light
   treatment; emphasising a caveats section as a climax would say the
   wrong thing about it. */
section.finale{
  background-color:#14132b;          /* fallback: the gradient is decoration, this is the floor */
  background-image:linear-gradient(165deg, #14132b 0%, #1b1a3a 52%, #221f45 100%);
  border-block:none;
  position:relative; overflow:hidden;
}
section.finale::before{     /* same dotted field as the hero */
  content:''; position:absolute; inset:0; pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.10) 1.3px, transparent 1.3px);
  background-size:24px 24px;
  -webkit-mask-image:radial-gradient(110% 90% at 12% 0%, #000 0%, transparent 62%);
          mask-image:radial-gradient(110% 90% at 12% 0%, #000 0%, transparent 62%);
}
section.finale > .wrap{ position:relative }
section.finale h2{ color:#fff }
section.finale .section-tag{ color:#a7a3ff; opacity:1 }
/* Opaque, and deliberately not translucent.
   A translucent card means the legibility of light text depends on an
   ancestor painting a dark background — so the day that background loses
   a specificity fight, or a stale stylesheet disagrees with the markup,
   you get light text on a light card and no error anywhere. That is
   exactly what shipped. An opaque card cannot fail that way: the text
   and the surface it sits on are set by the same rule.

   No backdrop-filter either. It bought a 2px blur and, on an element
   that is simultaneously animating opacity, it is a known source of
   washed-out compositing. */
section.finale .closing{
  background:#191834;
  border:1px solid rgba(255,255,255,.10);
  box-shadow:0 30px 70px rgba(0,0,0,.32);
}
section.finale .closing p{ color:#d3d1ee }
section.finale .closing p:last-child{ color:#fff }
section.finale strong{ color:#fff }
section.finale em{ color:#d9d7f4 }

/* ---------- 16. the icon chips carry the brand ---------- */
/* They were white squares holding a purple line icon, which reads as an
   absence next to a card that has a gradient somewhere else on screen. */
.icon-chip{
  background:var(--drm-gradient);
  box-shadow:0 10px 24px rgba(103,101,242,.34);
}
.icon-chip svg{ stroke:#fff }

/* ---------- 17. cards with an edge ---------- */
.card{ border-radius:18px }
.card > .icon-chip + h3{ margin-top:2px }
.grid > .card{ position:relative; overflow:hidden }
.grid > .card::before{        /* gradient hairline along the top edge */
  content:''; position:absolute; inset:0 0 auto 0; height:2px;
  background:var(--drm-gradient); opacity:0;
  transition:opacity .24s ease;
}
@media (hover:hover){
  .grid > .card:hover::before{ opacity:1 }
}

/* ---------- 18. grain ---------- */
/* Large flat gradients band on cheap panels. A little noise over the top
   breaks the banding up and costs one data URI — which img-src 'self'
   data: already permits, so no policy change. */
body::after{
  content:''; position:fixed; inset:0; z-index:98; pointer-events:none;
  opacity:.032;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
@media print{ body::after{ display:none !important } }

/* ---------- 19. figure and ground on the light sections ---------- */
/* Alternating the surface in 14 put white cards onto a white section —
   the worked-example closing block and the three team cards. They keep
   their border and shadow so nothing disappears, but a white card on a
   white field has no figure/ground separation left and reads as flat.
   Inverting the fill restores it: the card becomes the tinted shape and
   the section is the white one.

   Same reason the arc dots need a new halo. Their ring is painted in
   --drm-bg to punch the timeline out from behind them, which is only
   invisible while the section behind is actually --drm-bg. */
main > section:nth-of-type(even):not(.finale) .card,
main > section:nth-of-type(even):not(.finale) .principle,
main > section:nth-of-type(even):not(.finale) .closing{
  background:var(--drm-bg);
  border-color:#e4e6f2;
}
main > section:nth-of-type(even):not(.finale) .icon-chip{ background:var(--drm-gradient) }
main > section:nth-of-type(even):not(.finale) .arc-item::before{
  box-shadow:0 0 0 4px #fff, 0 6px 14px rgba(103,101,242,.28);
}

/* ---------- 20. the stat band ---------- */
/* Four numbers directly under the hero. They were all in the prose already
   and none of them was findable there, which on a page arguing that the
   claims are measured is the wrong thing to bury.

   The figures use the same background-clip:text gradient as the role line,
   which means the same rule applies: no filter on .fig, ever. */
.statband{
  background:#fff;
  border-bottom:1px solid var(--drm-card-border);
  padding:34px 0;
}
.statband .wrap{
  display:grid; gap:28px;
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
}
.stat{ display:flex; flex-direction:column; gap:6px }
.fig{
  font-size:clamp(34px, 3.5vw, 46px); font-weight:600; line-height:1;
  letter-spacing:-.03em;
  background:var(--drm-gradient);
  -webkit-background-clip:text; background-clip:text; color:transparent;
  font-variant-numeric:tabular-nums;
}
.fig em{ font-style:normal; font-size:.62em; letter-spacing:0 }
.cap{
  font-size:13px; line-height:1.5; color:var(--drm-muted);
  max-width:22rem;
}

/* Separators rather than boxes — the numbers are the figure here, and a card
   around each one would give four small panels equal weight to the five real
   cards further down. */
@media (min-width:721px){
  .stat + .stat{ padding-left:28px; border-left:1px solid var(--drm-card-border) }
}
@media (max-width:720px){
  .statband{ padding:26px 0 }
  .statband .wrap{ grid-template-columns:repeat(2, 1fr); gap:22px }
  .cap{ font-size:12.5px }
}
