/* Site-specific layout for the parts the design system does not template: open roles,
   the Formspree states and the contact columns. Reads only design-system tokens.
   Add new values to the design system first, never here. */

/* The full company name is longer than the system's short lockup, so keep it on one line. */
.brand { flex: none; }
.brand__name { white-space: nowrap; }
@media (max-width: 639px) { .brand__name { white-space: normal; width: min-content; line-height: 1.1; } }
.nav__bar { gap: var(--space-3); }
.nav__bar > nav { margin-left: auto; } /* the links sit on the right, beside the Contact button */

/* A tonal ground while an inline image loads. */
.feature-index__media img, .editorial__media img, .story img { background: var(--surface-sunken); }

/* Feature rows without numbers (.feature-index--plain): the number's column folds into the heading's, so the
   heading starts at the page edge. On desktop the split is 4:6:2 of twelve, which puts the text on column 5,
   in line with the section heads above it. */
.feature-index--plain .feature-index__item { grid-template-columns: 1fr; }
.feature-index--plain .feature-index__media { grid-column: 1; }
@media (min-width: 768px) and (max-width: 1023px) {
  .feature-index--plain .feature-index__item { grid-template-columns: 1fr 12rem; grid-template-rows: auto 1fr; } /* the text sits under its heading, not spread down the photo */
  .feature-index--plain .feature-index__media { grid-column: 2; }
}
@media (min-width: 1024px) {
  .feature-index--plain .feature-index__item { grid-template-columns: 4fr 6fr 2fr; }
  .feature-index--plain .feature-index__item > .t-body-sm { grid-column: 2; }
  .feature-index--plain .feature-index__media { grid-column: 3; }
}
/* From tablet up the rows alternate: every second row has its photo on the left. The words keep their reading
   order, heading then text, to the right of it. */
@media (min-width: 768px) and (max-width: 1023px) {
  .feature-index--plain .feature-index__item:nth-child(even) { grid-template-columns: 12rem 1fr; }
  .feature-index--plain .feature-index__item:nth-child(even) > :not(.feature-index__media) { grid-column: 2; }
  .feature-index--plain .feature-index__item:nth-child(even) .feature-index__media { grid-column: 1; grid-row: 1 / span 2; }
}
@media (min-width: 1024px) {
  .feature-index--plain .feature-index__item:nth-child(even) { grid-template-columns: 2fr 4fr 6fr; }
  .feature-index--plain .feature-index__item:nth-child(even) .feature-index__media { grid-column: 1; }
  .feature-index--plain .feature-index__item:nth-child(even) > h3 { grid-column: 2; grid-row: 1; }
  .feature-index--plain .feature-index__item:nth-child(even) > .t-body-sm { grid-column: 3; grid-row: 1; }
}

/* Section heads that sit directly on the section's edge. */
.section-head--flush { margin-bottom: 0; }
.section-head--compact { margin-bottom: var(--space-8); }

/* Questions: under the section head's content column. Column 4 stays empty. */
.accordion-col { grid-column: 1 / -1; }
@media (min-width: 1024px) { .accordion-col { grid-column: 5 / span 8; } }

/* Contact: the booking page.
   From 1024px the chairs photograph fills the screen under the navbar. The heading sits on its plain wall and the
   form in a frosted card beneath it, clear of the chairs: anchored right where the screen is wide, so the chairs
   stay in view (columns 1 to 5), anchored left elsewhere (columns 1 to 7).
   Below 1024px the photograph is a band across the top, showing the chairs, with the heading on its wall; the card
   overlaps the band's lower edge.
   In the card the fields are clear and fill white under the pointer, while typing and once they hold text. */
.contact-stage { --band: clamp(340px, 60svh, 560px); position: relative; padding-bottom: var(--space-16); background: var(--surface-subtle); }
.contact-stage__media { position: absolute; inset: 0 0 auto; height: var(--band); overflow: hidden; }
.contact-stage__media img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 80% 100%; }
.contact-stage__media::after { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, color-mix(in srgb, var(--surface-page) 40%, transparent), transparent 65%); } /* a light veil behind the words */
.contact-stage__content { position: relative; z-index: 1; }
.contact-stage__panel { grid-column: 1 / -1; display: grid; }
.contact-stage__head { display: grid; gap: var(--space-3); align-content: start; min-height: calc(var(--band) - var(--space-10)); padding-top: var(--space-8); }
.contact-stage__head .t-h1 { font-size: calc((var(--fs-h1) + var(--fs-h2)) / 2); } /* a step down from the full headline size, halfway to h2 */
.contact-stage .t-lead { color: var(--text-primary); } /* the grey tone falls under 4.5:1 on the wall */

.contact-card { padding: var(--space-6); border: var(--hairline) solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--surface-page); box-shadow: var(--shadow-overlay); }
.contact-card .form { gap: var(--space-4); max-width: none; }
.contact-card .form__row { gap: var(--space-4); }
.contact-card .textarea { min-height: calc(var(--space-16) + var(--space-2)); }
.contact-card .input, .contact-card .select, .contact-card .textarea { background-color: transparent; transition-duration: var(--duration-enter); }
.contact-card .input:hover, .contact-card .select:hover, .contact-card .textarea:hover,
.contact-card .input:focus, .contact-card .select:focus, .contact-card .textarea:focus,
.contact-card .input:not(:placeholder-shown), .contact-card .textarea:not(:placeholder-shown) { background-color: var(--surface-page); }
.contact-card__foot { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3) var(--space-5); }
.contact-card__foot .t-body-sm { flex: 1 1 14rem; margin: 0; } /* beside the button over two lines where it fits, under it where it does not */

@media (min-width: 1024px) {
  .contact-stage { display: grid; align-content: center; min-height: calc(100svh - var(--nav-height)); padding-block: var(--space-6); overflow: hidden; background: var(--surface-sunken); }
  .contact-stage__media { inset: 0; height: auto; }
  .contact-stage__media img { object-position: 0% 100%; }
  .contact-stage__panel { grid-column: 1 / span 7; gap: var(--space-6); max-width: 640px; }
  .contact-stage__head { min-height: 0; padding-top: 0; }
  .contact-card { border-color: color-mix(in srgb, var(--surface-page) 60%, transparent); background: color-mix(in srgb, var(--surface-page) 72%, transparent);
    -webkit-backdrop-filter: blur(20px) saturate(1.15); backdrop-filter: blur(20px) saturate(1.15); }
}
@media (min-width: 1280px) and (min-aspect-ratio: 8/5) {
  .contact-stage__media img { object-position: 100% 100%; }
  .contact-stage__panel { grid-column: 1 / span 5; }
}
.contact-ways { list-style: none; margin: 0; padding: var(--space-6) 0 0; display: grid; gap: var(--space-6) var(--gutter); border-top: var(--hairline) solid var(--border-subtle); }
.contact-ways li { display: grid; gap: var(--space-2); justify-items: start; }
@media (min-width: 768px) { .contact-ways { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Formspree fills these. Empty ones stay out of the layout; errors never rely on colour alone. */
.form__row { align-items: start; }
.notice:empty { display: none; }
.notice:not(:empty) { margin-bottom: var(--space-6); }
.field__error:not(:empty) { display: block; }
.field__error:not(:empty)::before { content: "Error: "; }
/* The Formspree library injects its own message colours; these restore the system's status tokens. */
.notice.notice--success[data-fs-active] { display: flex; padding: var(--space-4); border: 0; border-radius: var(--radius-sm); background: var(--status-success-bg); color: var(--status-success); }
.notice.notice--danger[data-fs-active] { display: flex; padding: var(--space-4); border: 0; border-radius: var(--radius-sm); background: var(--status-danger-bg); color: var(--status-danger); }
.field .field__error[data-fs-active] { display: block; padding: 0; border: 0; background: none; color: var(--status-danger); font-size: var(--fs-body-sm); }

/* Open roles, on the accordion. */
.job-summary { display: grid; gap: var(--space-1); min-width: 0; }
.job-meta { display: flex; flex-wrap: wrap; gap: 0 var(--space-3); }
.accordion .job-body { display: grid; gap: var(--space-8) var(--gutter); max-width: none; }
.job-body h4 { margin-bottom: var(--space-3); color: var(--text-primary); }
.job-intro { max-width: var(--measure-body); }
@media (min-width: 768px) {
  .accordion .job-body { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .job-intro, .job-apply { grid-column: 1 / -1; }
}
.plain-list { margin: 0; padding-left: var(--space-5); display: grid; gap: var(--space-2); font-size: var(--fs-body-sm); line-height: 1.55; color: var(--text-secondary); }

.measures strong { font-weight: 500; color: var(--text-primary); }

/* .measures resets its margin after .stack sets it; keep the space above the first hairline. */
.stack > .measures { margin-top: var(--stack, var(--space-4)); }

/* Services: one row per data service, the photograph on the left, the title and its details beside it. */
.offer-list { list-style: none; margin: 0; padding: 0; border-top: var(--hairline) solid var(--border-subtle); }
.offer-list__item { row-gap: var(--space-6); padding-block: var(--space-8); border-bottom: var(--hairline) solid var(--border-subtle); }
.offer-list__media, .offer-list__body { grid-column: 1 / -1; }
.offer-list__media img { display: block; width: 100%; max-width: 28rem; height: auto; aspect-ratio: 4 / 5; object-fit: cover; border-radius: var(--radius-md); background: var(--surface-sunken); }
.offer-list__body { display: grid; gap: var(--space-8); align-content: start; }
.detail-columns { margin: 0; display: grid; gap: var(--space-6) var(--gutter); }
.detail-columns > div { display: grid; gap: var(--space-2); align-content: start; }
.detail-columns dt { color: var(--text-muted); }
.detail-columns dd { margin: 0; }
@media (min-width: 768px) {
  .offer-list__media { grid-column: 1 / span 3; }
  .offer-list__body { grid-column: 4 / -1; }
}
@media (min-width: 1024px) {
  .offer-list__media { grid-column: 1 / span 3; }
  .offer-list__body { grid-column: 4 / -1; }
  .detail-columns { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Home, process: the plain steps, with the light blue tint coming over the one under the pointer. The tint
   sits behind the step and past its edges, so the words do not move. */
.steps--hover .steps__item { position: relative; isolation: isolate; }
.steps--hover .steps__item::before { content: ""; position: absolute; z-index: -1; inset: 0 calc(var(--gutter) / -2) calc(var(--space-4) * -1); background: var(--blue-50); border-radius: 0 0 var(--radius-md) var(--radius-md); opacity: 0; transition: opacity var(--duration-base) var(--ease-standard); }
@media (hover: hover) { .steps--hover .steps__item:hover::before { opacity: 1; } }

/* Careers hero: the group fills the right of the frame, so the words only sit on the photograph
   where the wall beside them is clear. Everywhere else they stack above it. */
.hero--stacks { background: var(--surface-page); }
@media (min-width: 1280px) and (min-aspect-ratio: 8/5) {
  .hero--stacks .hero__content .stack { grid-column: 1 / span 5; }
  .hero--stacks .t-lead { max-width: 32ch; }
}
@media not all and (min-width: 1280px) and (min-aspect-ratio: 8/5) {
  .hero--stacks { min-height: 0; }
  .hero--stacks::after { display: none; }
  .hero--stacks .hero__media { position: static; }
  .hero--stacks .hero__media img { height: auto; max-height: 85svh; }
  .hero--stacks .hero__content { padding-bottom: var(--space-12); }
}


/* Home: one row per service, the title beside a photograph that fills the rest of the row.
   Rows alternate from 1024px. 4:5 images on phones, wide images cropped to 3:1 from 768px, anchored by --focus. */
.service-rows { list-style: none; margin: 0; padding: 0; border-top: var(--hairline) solid var(--border-subtle); }
.service-row { row-gap: var(--space-6); align-items: center; padding-block: var(--space-8); border-bottom: var(--hairline) solid var(--border-subtle); }
.service-row__title, .service-row__media { grid-column: 1 / -1; }
.service-row__media img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 5; object-fit: cover; object-position: var(--focus, 50% 50%); border-radius: var(--radius-lg); background: var(--surface-sunken); }
@media (min-width: 768px) { .service-row__media img { aspect-ratio: 3 / 1; } }
@media (min-width: 1024px) {
  .service-row__title { grid-column: 1 / span 4; grid-row: 1; }
  .service-row__media { grid-column: 5 / span 8; grid-row: 1; }
  .service-row:nth-child(even) .service-row__media { grid-column: 1 / span 8; }
  .service-row:nth-child(even) .service-row__title { grid-column: 9 / span 4; }
}

/* Home questions: the heading, the list and the question and answer text are all centred. */
.faq .section-head__body { grid-column: 1 / -1; text-align: center; }
.faq .section-head__body .t-h2 { margin-inline: auto; }
.faq .accordion-col { grid-column: 1 / -1; }
@media (min-width: 1024px) { .faq .accordion-col { grid-column: 3 / span 8; } }
.faq summary { position: relative; justify-content: center; padding-inline: var(--space-8); text-align: center; text-wrap: balance; }
.faq .acc-icon { position: absolute; right: 0; top: 50%; margin-top: -7px; }
.faq .acc-body { margin-inline: auto; text-align: center; text-wrap: pretty; }

/* Services: applied AI, centred on the page. */
.applied-ai .section-head__body { grid-column: 1 / -1; text-align: center; }
.applied-ai .t-body { margin-inline: auto; }
/* Where the closing words stack above their photograph, that section's own top space is enough. */
@media not all and (min-width: 1280px) and (min-aspect-ratio: 8/5) { .applied-ai { padding-bottom: 0; } }

/* Centre only a section's heading and the line under it. */
.head-centred .section-head__body { grid-column: 1 / -1; text-align: center; }
.head-centred .section-head__body .t-h2, .head-centred .section-head__body .t-lead { margin-inline: auto; }

/* Careers, open roles: centred like the questions. The details inside each role stay left-aligned so the lists read easily. */
.roles-centred { text-align: center; }
.roles-centred .t-body { margin-inline: auto; }
.roles-centred .job-summary { justify-items: center; }
.roles-centred .job-meta { justify-content: center; }
.faq.roles-centred .job-body { text-align: left; }

/* Closing photographs. On wide screens the words sit on the plain left of the photograph; below that they
   stack above it, so they never cross the subject. */
.closing-photo__media img { display: block; width: 100%; height: auto; background: var(--surface-sunken); }
.closing-photo { padding-bottom: 0; }
.closing-photo__media { margin-top: var(--space-12); }
.closing-photo__note { padding-block: var(--space-4); }
.closing-photo__note .t-body-sm { color: var(--text-primary); }
.closing-photo__text .link--quiet { text-decoration-color: currentColor; }
@media (min-width: 1280px) and (min-aspect-ratio: 8/5) {
  .closing-photo { position: relative; display: grid; align-content: center; min-height: min(45vw, 760px); padding-block: var(--space-16); }
  .closing-photo__text { position: relative; z-index: 1; }
  .closing-photo__text .t-lead { max-width: 32ch; color: var(--text-primary); }
  .closing-photo__text .t-h1 { max-width: 12ch; }
  .closing-photo__media { position: absolute; inset: 0; margin-top: 0; }
  .closing-photo__media img { height: 100%; object-fit: cover; object-position: 100% 50%; }
  .closing-photo__note { position: absolute; left: 0; right: 0; bottom: var(--space-4); z-index: 1; padding-block: 0; }
  .closing-photo__note .t-body-sm { max-width: 46vw; }
  #apply .closing-photo__text { padding-bottom: var(--space-16); } /* sits slightly above centre, away from the line at the bottom */
  /* Home: the words stay above the telephone cord, which crosses the left of the photograph. */
  .closing-photo--above-line { align-content: start; padding-top: var(--space-12); }
  .closing-photo--above-line .closing-photo__text .t-lead { max-width: 44ch; }
  .closing-photo .btn--ghost { background: var(--surface-page); }
  .closing-photo .btn--ghost:hover { background: var(--surface-sunken); }
}

/* Home questions: the heading stays on one line at every width, scaling down on narrow phones. */
#questions-title { max-width: none; white-space: nowrap; font-size: min(var(--fs-h2), 6.8vw); }

/* Home: the platforms we work on, a sliding bar under the hero. Logos take the logo row's greyscale treatment.
   The track moves by half its width, which is one full list, so the loop has no seam. */
.platforms { border-bottom: var(--hairline) solid var(--border-subtle); }
.platforms__bar { display: flex; align-items: center; gap: var(--space-6); padding-block: var(--space-6); }
.platforms__label { flex: none; color: var(--text-muted); }
.platforms__viewport { flex: 1; min-width: 0; overflow: hidden; mask-image: linear-gradient(to right, transparent, #000 var(--space-8), #000 calc(100% - var(--space-8)), transparent); }
.platforms__track { display: flex; width: max-content; animation: platforms-slide 67s linear infinite; } /* about 35px a second; lengthen it when logos are added */
.platforms__viewport:hover .platforms__track { animation-play-state: paused; }
.platforms__list { display: flex; flex: none; align-items: center; gap: var(--space-20); margin: 0; padding: 0 var(--space-20) 0 0; list-style: none; }
.platforms__list li { display: flex; flex: none; align-items: center; gap: var(--space-2); font-size: var(--fs-body-sm); font-weight: 500; color: var(--text-muted); white-space: nowrap; }
.platforms__list img { display: block; flex: none; max-width: none; filter: grayscale(1); opacity: .72; }
@keyframes platforms-slide { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
  .platforms__viewport { mask-image: none; }
  .platforms__track { width: auto; animation: none; }
  .platforms__list { flex-wrap: wrap; flex: 1; row-gap: var(--space-4); column-gap: var(--space-10); padding-right: 0; }
  .platforms__list[aria-hidden="true"] { display: none; }
}

/* Phones held upright: the top heroes carry their words on a tall 1:2 photograph, as on desktop. */
@media (max-width: 767px) and (orientation: portrait) {
  .hero--stacks { min-height: 200vw; } /* the whole photograph, so the group always sits below the words */
  .hero--stacks .hero__media { position: absolute; }
  .hero--stacks .hero__media img { height: 100%; max-height: none; object-position: 50% 100%; }
  .hero--center .hero__content { padding-inline: 18%; } /* keeps the words inside the plain salt pan */
}

/* Footer: the LinkedIn logo sits beside its name, in the column's link colour. */
.footer__col a.footer__linkedin { gap: var(--space-2); }
.footer__linkedin svg { flex: none; }

/* ---------- Motion -----------------------------------------------------------------------------------------------
   Everything here moves only opacity and transforms, which the browser hands to the graphics card, so nothing
   repaints while it plays and the page never waits on it. Timings are multiples of the system's motion tokens, on
   its standard curve. The wheel's glide, the entrances' timing and the buttons' lean are driven from site.js.
   Reduced motion: no entrances, no drift, no zoom; the system's own rule cuts any remaining transition short. */
:root {
  --motion-reveal: calc(var(--duration-enter) * 3); /* 840ms: an entrance fading in, a hover easing in */
  --motion-settle: calc(var(--duration-enter) * 4); /* 1120ms: the same block settling into place */
  --motion-photo: calc(var(--duration-enter) * 5);  /* 1400ms: a photograph easing back to size */
  --motion-step: calc(var(--duration-fast) * .75);  /* 90ms: between blocks that arrive together */
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; } /* in-page links, such as the navbar's FAQ on the home page */

  /* Page to page: the content crossfades under a navbar that stays where it is. */
  @view-transition { navigation: auto; }
  .nav { view-transition-name: site-nav; }
  ::view-transition-old(root), ::view-transition-new(root) { animation-duration: calc(var(--duration-enter) * 1.5); animation-timing-function: var(--ease-standard); }

  /* On arrival: the page's opening words rise in one after the other; the hero photograph eases back from a
     slight zoom. */
  .enter { animation: fade-in var(--motion-settle) ease-out var(--motion-step) both, rise-in var(--motion-photo) var(--ease-standard) var(--motion-step) both; }
  .enter ~ .enter { animation-delay: calc(var(--motion-step) * 3); }
  .contact-card.enter { animation-delay: calc(var(--motion-step) * 6); } /* contact: heading, line, then the form */
  .hero__media { overflow: hidden; }
  .hero__media img, .contact-stage__media img { animation: settle-in calc(var(--duration-enter) * 6) var(--ease-standard) both; }

  /* Entrances while scrolling. site.js adds .is-in as a block comes into view, staggering blocks that arrive
     together (--reveal-order), and drops both classes once it has settled, so hover effects are untouched.
     The fade runs on a gentle ease-out, so the words come up gradually rather than all at once, while the rise
     glides in on the standard curve. */
  .js .reveal { transform: none; translate: 0 var(--space-10);
    transition: opacity var(--motion-settle) ease-out, translate var(--motion-photo) var(--ease-standard);
    transition-delay: calc(var(--reveal-order, 0) * var(--motion-step)); }
  .js .reveal.is-in { translate: none; }
  /* A photograph in an entering block starts closer and eases back as the block rises. */
  .js .reveal:not(.is-in) .photo-zoom img, .js .photo-zoom.reveal:not(.is-in) img { scale: 1.25; }
  /* Process steps: each step's top line draws in from the left, then hands over to the real border. */
  .js .steps__item.reveal { position: relative; border-top-color: transparent; }
  .js .steps__item.reveal::after { content: ""; position: absolute; left: 0; right: 0; top: calc(var(--hairline) * -1); height: var(--hairline);
    background: var(--border-subtle); transform-origin: left; scale: 0 1; transition: scale var(--motion-photo) var(--ease-standard); transition-delay: inherit; }
  .js .steps__item.reveal:last-child::after { background: var(--text-primary); }
  .js .steps__item.reveal.is-in::after { scale: 1 1; }

  /* Photographs that zoom in gently under the pointer (.photo-zoom). The picture sits slightly enlarged in its
     frame so it can also drift with the scroll without showing an edge. */
  .photo-zoom img { scale: 1.1; transition: scale var(--motion-photo) var(--ease-standard); }
  @media (hover: hover) and (pointer: fine) {
    .photo-zoom:hover img { scale: 1.16; transition-duration: var(--motion-reveal); }
  }

  /* Photographs drift a little slower than the page as it scrolls (scroll-driven, so it runs off the main thread;
     browsers without it keep the photographs still). The hero's photograph sinks and its words lift away. */
  @supports (animation-timeline: view()) {
    .photo-zoom img, .story img, .closing-photo__media img { animation: drift linear both; animation-timeline: view(); animation-range: cover; }
    .story, .closing-photo__media { overflow: hidden; }
    .story img, .closing-photo__media img { scale: 1.1; }
    .hero:not(.hero--stacks) .hero__media img { animation: settle-in calc(var(--duration-enter) * 6) var(--ease-standard) both, hero-drift linear both;
      animation-timeline: auto, scroll(root); animation-range: normal, 0 100vh; }
    .hero:not(.hero--stacks) .hero__content { animation: hero-out linear both; animation-timeline: scroll(root); animation-range: 0 80vh; }
  }

  /* Accordions open and close smoothly where the browser can animate to the content's height. */
  @supports selector(::details-content) and (interpolate-size: allow-keywords) {
    .accordion { interpolate-size: allow-keywords; }
    .accordion details::details-content { height: 0; opacity: 0; overflow: clip;
      transition: height var(--motion-reveal) var(--ease-standard), opacity var(--motion-reveal) var(--ease-standard), content-visibility var(--motion-reveal) allow-discrete; }
    .accordion details[open]::details-content { height: auto; opacity: 1; }
  }
}

/* The frame the zoom happens in. Corners match each photograph's own. */
.photo-zoom { position: relative; overflow: hidden; isolation: isolate; }
.photo-zoom picture { display: block; }
.photo-zoom img { display: block; }
.service-row__media.photo-zoom { border-radius: var(--radius-lg); }
.editorial__media.photo-zoom, .offer-list__media.photo-zoom { border-radius: var(--radius-md); }
.offer-list__media.photo-zoom { max-width: 28rem; }

@media (hover: hover) {
  /* Platforms bar: the logo under the pointer comes into colour (the bar pauses while the pointer is on it). */
  .platforms__list li { transition: color var(--motion-reveal) var(--ease-standard); }
  .platforms__list img { transition: filter var(--motion-reveal) var(--ease-standard), opacity var(--motion-reveal) var(--ease-standard); }
  .platforms__list li:hover { color: var(--text-primary); }
  .platforms__list li:hover img { filter: none; opacity: 1; }
}

@keyframes fade-in { from { opacity: 0; } }
@keyframes rise-in { from { translate: 0 var(--space-12); } }
@keyframes settle-in { from { scale: 1.12; } }
@keyframes drift { from { translate: 0 -4%; } to { translate: 0 4%; } }
@keyframes hero-drift { to { translate: 0 20%; } }
@keyframes hero-out { to { opacity: 0; translate: 0 var(--space-20); } }

@media print { .js .reveal { opacity: 1; transform: none; translate: none; } }

