/* ============================================================
   Luxshine — Factory page (vietnam-hair-factory.html), built from
   the designer's file "factory.ai" (user 2026-10-22: "redesign lại
   trang factory dựa theo y hệt trong file này"). It stands on the
   About page's pieces (css/about-page.css: the 1500px frame, the
   gold-bar titles, the silks, the photo curtains, the contact form);
   this file adds what is particular to the factory. Tokens only.
   Measures come from the artboard rendered 1920px wide.
   ============================================================ */

/* Section titles are 40px here (file ~42px), About's are 48. */

/* The two big titles (file ~74px; display-4 added to the DS for them, user 2026-10-22). Each line rises out of its own mask;
   the mask keeps room under the letters for g and y. */
.fx-big { margin: 0; font-family: var(--font-heading); font-weight: 400; font-size: var(--display-4-size); line-height: var(--display-4-lh); color: inherit; }
/* The mask is widened by the gold bar's overhang so the bar is not cut at either end. */
.fx-big__line { display: block; margin-inline: -.25em; padding-inline: .25em; overflow: hidden; }
.fx-big__in { display: block; }
.fx-big--caps { text-transform: uppercase; }
/* A hyphenated pair (Salon-Grade, Cool-Tone) never splits at its hyphen. */
.fx-keep { white-space: nowrap; }
/* The gold bar sits under the last line only, as wide as that line and a little past both ends (file). */
.fx-mark { margin-inline: -.25em; padding-inline: .25em; background: linear-gradient(var(--color-primary), var(--color-primary)) 0 100% / 100% .36em no-repeat; -webkit-box-decoration-break: clone; box-decoration-break: clone; }

/* ---------- Banner: the factory from the air, the film behind the play button ---------- */
.fx-banner { position: relative; overflow: hidden; background: var(--surface-dark-deep); }
/* As tall as the About page's banner (comment cmvor69e1hqsa: "thu height lại bằng với height banner trang about us"),
   the film covering it; on a desktop it also runs up behind the see-through header (below), so the part under the
   header matches the About banner. */
.fx-banner__media { position: relative; height: max(35.19vw, 240px); }
.fx-banner__media img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; }
/* The film (Luxshine's YouTube film s5cykt0zs0U, kept on the site: comment cmvkshntv00p3) is 2.35:1; it covers the 16:9
   frame, cut at the sides, and fades in over the photo once it plays. Paused, it keeps its frame. */
.fx-banner__film { position: absolute; inset: 0; display: block; width: 100%; height: 100%; object-fit: cover; opacity: 0; pointer-events: none; transition: opacity .6s ease; }
.fx-banner__film.is-on { opacity: 1; }
.fx-banner__film[controls] { pointer-events: auto; object-fit: contain; background: var(--surface-dark-deep); } /* reduced motion: started from the play button, shown whole with the browser's controls */
/* Lower right, once the film shows: the sound button, with a slider for the level that opens on hover or keyboard focus
   (user 2026-10-23: "Khi hover vào thì cho phép user tăng giảm các mức âm lượng"). A native range, so arrow keys work. */
.fx-banner__ctrl { position: absolute; right: var(--space-6); bottom: var(--space-6); z-index: 2; display: flex; align-items: center; gap: var(--space-2); }
.fx-banner__ctrl[hidden] { display: none; }
.fx-banner__btn { display: grid; place-items: center; width: var(--space-12); height: var(--space-12); padding: 0; border: 1px solid color-mix(in srgb, var(--color-white) 45%, transparent); border-radius: var(--radius-round); background: color-mix(in srgb, var(--color-black) 55%, transparent); color: var(--color-white); cursor: pointer; transition: background-color .25s ease, color .25s ease; }
.fx-banner__btn:is(:hover, :focus-visible) { background: var(--color-primary); color: var(--color-black); }
.fx-banner__btn:focus-visible { outline: 2px solid var(--color-white); outline-offset: 2px; }
.fx-banner__glyph { width: var(--space-6); height: var(--space-6); }
.fx-banner__glyph--sound, .is-sound .fx-banner__glyph--muted { display: none; }
.is-sound .fx-banner__glyph--sound { display: block; }
.fx-banner__level { box-sizing: content-box; width: var(--space-24); height: var(--space-12); margin: 0; padding-inline: var(--space-3); border-radius: var(--radius-round); background: color-mix(in srgb, var(--color-black) 55%, transparent); accent-color: var(--color-primary); cursor: pointer; opacity: 0; translate: var(--space-2) 0; pointer-events: none; transition: opacity .25s ease, translate .25s ease; }
.fx-banner__ctrl:is(:hover, :focus-within) .fx-banner__level { opacity: 1; translate: 0 0; pointer-events: auto; }
.fx-banner__level:focus-visible { outline: 2px solid var(--color-white); outline-offset: 2px; }
/* Touch screens have no hover, and their own volume keys: the sound button alone. */
@media (hover: none) { .fx-banner__level { display: none; } }
/* The film itself is a button (js/factory-page.js): a click stops / starts it, a double click takes it full screen. */
.fx-banner__hit { position: absolute; inset: 0; z-index: 1; width: 100%; padding: 0; border: 0; background: none; cursor: pointer; touch-action: manipulation; }
.fx-banner__hit:focus-visible { outline: 2px solid var(--color-white); outline-offset: calc(var(--space-1) * -1); }
/* Full screen shows the whole film, on black. */
.fx-banner:fullscreen .fx-banner__media { width: 100%; height: 100%; animation: none; }
.fx-banner:fullscreen .fx-banner__film { object-fit: contain; }
.fx-banner:fullscreen::before { content: none; }
.fx-banner__play { position: absolute; left: 50%; top: 50%; z-index: 1; display: grid; place-items: center; width: clamp(var(--space-24), 12.5vw, 240px); height: clamp(var(--space-24), 12.5vw, 240px); padding: 0; border: 0; border-radius: var(--radius-round); background: none; color: color-mix(in srgb, var(--color-black) 72%, transparent); cursor: pointer; translate: -50% -50%; transition: color .3s ease, scale .5s var(--ab-ease); }
.fx-banner__play svg { width: 100%; height: 100%; }
.fx-banner__play path { fill: currentColor; } /* the file's triangle is solid */
.fx-banner__play::after { content: ""; position: absolute; inset: 8%; border: 2px solid currentColor; border-radius: var(--radius-round); opacity: 0; pointer-events: none; }
.fx-banner__play:is(:hover, :focus-visible) { color: var(--color-primary); scale: 1.06; }
.fx-banner__play:focus-visible { outline: 2px solid var(--color-white); outline-offset: var(--space-1); }
.fx-banner__play[hidden] { display: none; }

/* ---------- One Integrated Manufacturing Ecosystem: title 557, gap 48, copy 895 ---------- */
.fx-eco { padding: var(--space-12) 0 var(--space-16); }
.fx-eco__grid { display: grid; grid-template-columns: minmax(0, 557fr) minmax(0, 895fr); column-gap: var(--space-12); align-items: start; }
.fx-eco .ab-copy { line-height: var(--h5-lh); } /* the file sets this copy at 30px leading */
/* What we make, behind a thin gold rule. */
.fx-eco__make { position: relative; margin-top: var(--space-6); padding-left: var(--space-3); }
.fx-eco__make::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--color-primary); transform-origin: 50% 0; }
.fx-eco__label { margin: 0; font-size: var(--h5-size); line-height: var(--h5-lh); font-weight: 600; color: var(--color-primary); }
.fx-eco__list { display: flex; flex-wrap: wrap; margin: var(--space-1) 0 0; padding: 0; list-style: none; font-size: var(--h6-size); line-height: var(--h6-lh); }
/* The bar follows each item, so a wrapped line ends on a bar instead of starting with one. */
.fx-eco__list li:not(:last-child)::after { content: "|"; margin-inline: var(--space-1); }

/* ---------- From Vietnamese Hair to Salon-Grade Extensions: five steps on dark silk ---------- */
.fx-steps { padding: var(--space-12) 0 var(--space-16); }
.fx-steps .ab-title { margin-bottom: var(--space-12); }
.fx-steps__list { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--space-8); margin: 0; padding: 0; list-style: none; }
.fx-step { display: flex; flex-direction: column; align-items: center; min-width: 0; text-align: center; }
/* Each step's own photo stands in the frame until its film arrives (user 2026-10-22: "Anh/chị gửi 5 video sau"). */
.fx-step__media { width: 100%; aspect-ratio: var(--ty-le-buoc_san_xuat); background: var(--primary-50); container-type: size; }
/* Each frame is a film: the step's own upright Short of Luxshine's channel, kept on the site (comment cmvoqvrublasb:
   YouTube was too slow), 9:16 covering the 2:3 frame. It plays muted under the pointer and shows once it is playing. */
.fx-step__film { position: absolute; inset: 0; z-index: 1; display: block; width: 100%; height: 100%; object-fit: cover; opacity: 0; pointer-events: none; transition: opacity .3s ease; }
.fx-step__media.is-on .fx-step__film.is-playing { opacity: 1; }
.fx-step__play { position: absolute; inset: 0; z-index: 2; display: grid; place-items: center; width: 100%; padding: 0; border: 0; background: none; color: var(--color-white); cursor: pointer; }
.fx-step__play-icon { width: var(--space-16); height: var(--space-16); filter: drop-shadow(0 0 var(--space-2) color-mix(in srgb, var(--color-black) 45%, transparent)); transition: opacity .3s ease, scale .4s var(--ab-ease); }
.fx-step__play:is(:hover, :focus-visible) .fx-step__play-icon { scale: 1.08; }
.fx-step__play:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -4px; }
/* The play mark stays (pulsing, in the motion block) while a film's first seconds play hidden, and goes once it shows. */
.fx-step__media.is-on:has(.fx-step__film.is-playing) .fx-step__play-icon { opacity: 0; animation: none; }
/* The step films' player (comment cmvoqvrublasb: "player full screen cho list video short này"): a native modal dialog
   over the whole window, the film upright and as tall as the window allows, previous / next beside it, the five steps
   in a row under it, the close button in the corner. */
.fx-reel { width: 100vw; max-width: none; height: 100svh; max-height: none; margin: 0; padding: var(--space-6) var(--space-6) var(--space-4); border: 0; background: color-mix(in srgb, var(--surface-dark-deep) 94%, transparent); color: var(--color-white); }
.fx-reel[open] { display: grid; grid-template-rows: minmax(0, 1fr) auto; row-gap: var(--space-4); justify-items: center; }
.fx-reel::backdrop { background: var(--surface-dark-deep); }
.fx-reel__stage { display: grid; grid-template-columns: auto auto auto; align-items: center; column-gap: var(--space-6); min-height: 0; height: 100%; }
.fx-reel__frame { display: grid; grid-template-rows: minmax(0, 1fr) auto; row-gap: var(--space-3); height: 100%; min-height: 0; margin: 0; }
.fx-reel__video { display: block; height: 100%; max-width: calc(100vw - var(--space-24) * 2); aspect-ratio: var(--ty-le-short); object-fit: contain; background: var(--color-black); }
.fx-reel__caption { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-1) var(--space-3); text-align: center; }
.fx-reel__count { font-weight: 600; font-size: var(--body-size); line-height: var(--body-lh); text-transform: uppercase; color: var(--color-primary); }
.fx-reel__title { font-weight: 700; font-size: var(--body-size); line-height: var(--body-lh); }
.fx-reel__nav, .fx-reel__close { display: grid; place-items: center; width: var(--space-12); height: var(--space-12); padding: 0; border: 1px solid color-mix(in srgb, var(--color-white) 40%, transparent); border-radius: var(--radius-round); background: color-mix(in srgb, var(--color-black) 40%, transparent); color: var(--color-white); cursor: pointer; transition: background-color .2s ease, color .2s ease, border-color .2s ease; }
.fx-reel__nav:is(:hover, :focus-visible), .fx-reel__close:is(:hover, :focus-visible) { border-color: var(--color-primary); background: var(--color-primary); color: var(--color-black); }
:is(.fx-reel__nav, .fx-reel__close, .fx-reel__thumb):focus-visible { outline: 2px solid var(--color-white); outline-offset: 2px; }
.fx-reel__icon { width: var(--space-6); height: var(--space-6); }
.fx-reel__close-form { position: absolute; top: var(--space-4); right: var(--space-4); margin: 0; }
.fx-reel__list { display: flex; justify-content: center; gap: var(--space-2); margin: 0; padding: 0; list-style: none; }
.fx-reel__thumb { position: relative; display: block; width: var(--space-12); aspect-ratio: var(--ty-le-buoc_san_xuat); padding: 0; overflow: hidden; border: 2px solid transparent; background: var(--color-black); cursor: pointer; opacity: .55; transition: opacity .2s ease, border-color .2s ease; }
.fx-reel__thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
.fx-reel__thumb-num { position: absolute; left: var(--space-1); bottom: var(--space-1); font-weight: 700; font-size: var(--caption-size); line-height: var(--caption-lh); color: var(--color-white); }
.fx-reel__thumb:hover, .fx-reel__thumb[aria-current="true"] { opacity: 1; }
.fx-reel__thumb[aria-current="true"] { border-color: var(--color-primary); }
/* Phones: previous / next sit low on either side of the film, over its edge; a swipe does the same (js). */
@media (max-width: 767px) {
  .fx-reel { padding: var(--space-12) var(--space-2) var(--space-3); }
  .fx-reel__stage { grid-template-columns: minmax(0, 1fr); position: relative; }
  .fx-reel__video { max-width: 100%; }
  .fx-reel__nav { position: absolute; top: 50%; z-index: 1; translate: 0 -50%; }
  .fx-reel__nav--prev { left: var(--space-2); }
  .fx-reel__nav--next { right: var(--space-2); }
  .fx-reel__close-form { top: var(--space-2); right: var(--space-2); }
}

.fx-step__num { display: grid; place-items: center; width: var(--space-8); height: var(--space-8); margin-top: var(--space-8); border-radius: var(--radius-round); background: var(--color-primary); color: var(--color-black); font-family: var(--font-heading); font-size: var(--h6-size); line-height: 1; }
/* In the R&D cards' label face (The need / The development), comment cmvjesu5sx5kk. */
/* Step names as on the homepage's steps (comment cmvksih58wmu0, 2026-10-23: "svn-gilroy", lines in balance): Gilroy
   bold, every name given two lines so the five descriptions start on one line. */
.fx-step__name { display: flex; align-items: center; justify-content: center; min-height: calc(var(--h5-lh) * 2); margin: var(--space-3) 0 0; font-family: var(--font-body); font-weight: 700; font-size: var(--h5-size); line-height: var(--h5-lh); text-wrap: balance; color: inherit; }
.fx-step__text { margin: var(--space-1) 0 0; font-size: var(--body-size); line-height: var(--body-lh); text-wrap: pretty; }

/* ---------- R&D Department + Latest from our R&D share one light silk ---------- */
.fx-rd { padding-top: var(--space-12); }
.fx-rd__lead { max-width: 46em; margin: var(--space-8) auto 0; font-size: var(--h5-size); line-height: var(--h5-lh); text-align: center; text-wrap: pretty; }
.fx-rd__lead strong { font-weight: 700; }
/* The development path: a beige band across the screen, seven gold rounds joined by arrows. */
.fx-flow { margin-top: var(--space-12); padding: var(--space-12) 0 var(--space-8); background: var(--surface-beige); }
.fx-flow__list { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); width: min(83.6vw, 1605px); margin: 0 auto; padding: 0; list-style: none; }
.fx-flow__step { position: relative; display: flex; flex-direction: column; align-items: center; min-width: 0; text-align: center; }
.fx-flow__icon { display: grid; place-items: center; width: var(--space-24); height: var(--space-24); border-radius: var(--radius-round); background: var(--primary-300); color: var(--color-black); }
/* Each of the designer's icons at its own size in the file (its viewBox is in points: 1pt = 1.778px at 1920). */
.fx-flow__icon { --fx-icon-k: 1.778px; }
.fx-flow__icon svg { width: calc(var(--vbw) * var(--fx-icon-k)); height: calc(var(--vbh) * var(--fx-icon-k)); }
.fx-flow__icon :is(g, path, circle) { stroke-width: 1.5; }
.fx-flow__arrow { position: absolute; left: 100%; top: calc(var(--space-12) - var(--space-4)); width: var(--space-8); height: var(--space-8); translate: -50% 0; color: var(--color-black); }
.fx-flow__arrow path { stroke-width: 1.5; }
.fx-flow__name { display: flex; align-items: center; justify-content: center; min-height: calc(var(--h6-lh) * 2); margin-top: var(--space-6); font-size: var(--h6-size); line-height: var(--h6-lh); }

.fx-latest { padding: var(--space-12) 0 var(--space-24); }
.fx-latest__title { margin: 0 0 var(--space-12); font-family: var(--font-heading); font-weight: 400; font-size: var(--sec-title-size); line-height: var(--sec-title-lh); text-transform: uppercase; text-align: center; }
.fx-rd-cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-12); }
.fx-rd-card { display: flex; flex-direction: column; min-width: 0; background: var(--surface-beige); }
.fx-rd-card__media { aspect-ratio: var(--ty-le-tin); }
/* Narrow side padding: the longest name (New Cool-Tone Colors) keeps to one line at 1920, as in the file. */
.fx-rd-card__body { display: flex; flex: 1; flex-direction: column; align-items: center; padding: var(--space-6) var(--space-3) var(--space-8); text-align: center; }
.fx-rd-card__name { margin: 0; font-family: var(--font-heading); font-weight: 400; font-size: var(--h3-size); line-height: var(--h3-lh); text-wrap: balance; }
.fx-rd-card__facts { margin: var(--space-6) 0 var(--space-8); }
.fx-rd-card__facts dt { font-family: var(--font-heading); font-size: var(--h5-size); line-height: var(--h5-lh); text-transform: uppercase; color: var(--color-primary); }
.fx-rd-card__facts dd { max-width: 14em; margin: var(--space-1) auto 0; font-size: var(--h6-size); line-height: var(--h6-lh); text-wrap: balance; }
.fx-rd-card__facts dd + dt { margin-top: var(--space-4); }
/* The site's own dark button (.btn .btn--dark in base.css, comment cmvjdtrkoaufo: "đồng bộ format nút"); here only placed. */
.fx-rd-card__cta { margin-top: auto; font-size: var(--nav-size); } /* 18px (comment cmvjeu5m2dzx6), the DS step added for the menu */
.fx-rd-card__cta:focus-visible { outline: 2px solid var(--color-black); outline-offset: 2px; }

/* ---------- Responsibly Ethical Sourced: words left, three photos overlapping right (843×644 at 1920) ---------- */
.fx-ethic { padding-block: var(--space-24); }
.fx-ethic__grid { display: grid; grid-template-columns: minmax(0, 658fr) minmax(0, 843fr); align-items: start; }
.fx-ethic__text { padding: var(--space-12) var(--space-6) 0 var(--space-3); }
.fx-ethic__copy { max-width: 23em; margin: var(--space-6) 0 0; font-size: var(--h5-size); line-height: var(--h5-lh); }
.fx-ethic__cluster { position: relative; aspect-ratio: var(--ty-le-cum_nguon_goc); }
/* Three 3:2 photos placed where the file puts them; each new one lies over the one before. */
.fx-ethic__photo { position: absolute; width: 52.67%; aspect-ratio: var(--ty-le-tin); }
.fx-ethic__photo--a { left: 8.3%; top: 0; z-index: 1; }
.fx-ethic__photo--b { left: 47.33%; top: 29.81%; z-index: 2; }
.fx-ethic__photo--c { left: 0; top: 53.26%; z-index: 3; }

/* ---------- Find the Right Hair for Your Salon + About's contact form ---------- */
/* Certificate (moved here from the homepage): on flat beige rather than the homepage's black silk, so it no longer
   runs on from the black Ethical Sourced block above (comment cmvksjfom6xhd, 2026-10-23). */
.fx-cert { background: var(--surface-beige); color: var(--text-primary); }
.fx-contact { padding: var(--space-24) 0 calc(var(--space-16) * 2); }
/* Words 575, air 192, form 690 (the file runs the form 13px past the frame; here it stops at it). */
.fx-contact__grid { display: grid; grid-template-columns: minmax(0, 575fr) minmax(0, 192fr) minmax(0, 690fr); padding-left: var(--space-12); align-items: start; }
.fx-contact .ab-form { grid-column: 3; margin-top: 0; }
.fx-contact__title { margin: 0; font-family: var(--font-heading); font-weight: 400; font-size: var(--sec-title-size); line-height: var(--sec-title-lh); text-transform: uppercase; }
.fx-contact__sub { margin: var(--space-2) 0 0; font-size: var(--h4-size); line-height: var(--h4-lh); font-weight: 600; }
.fx-needs { margin: var(--space-8) 0 0; padding: 0; list-style: none; }
.fx-need { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--space-3); }
.fx-need + .fx-need { margin-top: var(--space-12); }
.fx-need__icon { grid-row: 1; align-self: center; width: calc(var(--space-4) + var(--space-1)); height: calc(var(--space-4) + var(--space-1)); color: var(--color-black); }
.fx-need__icon :is(g, path) { stroke-width: 1.5; }
.fx-need__name { grid-column: 2; margin: 0; font-family: var(--font-heading); font-weight: 400; font-size: var(--h5-size); line-height: var(--h5-lh); text-transform: uppercase; }
.fx-need p { grid-column: 2; margin: var(--space-1) 0 0; font-size: var(--h6-size); line-height: var(--h6-lh); }

/* ============================================================
   Motion, in About's language: titles rise out of masks, curtains
   lift off photos, then each block tells its own order (steps one
   by one, the path round by round, the photos landing on each
   other). transform / opacity only, after scripting (.js), none
   with reduced motion.
   ============================================================ */
@media (prefers-reduced-motion: no-preference) {
  .fx-banner__media img { animation: ab-settle 2s var(--ab-ease) both; }
  /* The play button lands after the photo, then rings twice to say it can be pressed. */
  .js .fx-banner__play[data-reveal] { opacity: 0; scale: .6; transition: opacity .8s var(--ab-ease) .5s, scale 1.2s var(--ab-ease) .5s, color .3s ease; }
  .js .fx-banner__play[data-reveal].is-in { opacity: 1; scale: 1; }
  .js .fx-banner__play[data-reveal].is-in:is(:hover, :focus-visible) { scale: 1.06; transition: scale .5s var(--ab-ease), color .3s ease; }
  .js .fx-banner__play[data-reveal].is-in::after { animation: fx-ring 1.6s var(--ab-ease) 1.5s 2; }

  .js .fx-big[data-reveal] .fx-big__in { transform: translateY(110%); transition: transform 1.1s var(--ab-ease) calc(var(--l, 0) * .12s); }
  .js .fx-big[data-reveal].is-in .fx-big__in { transform: none; }
  .js .fx-eco__make[data-reveal]::before { transform: scaleY(0); transition: transform .9s var(--ab-ease) .35s; }
  .js .fx-eco__make[data-reveal].is-in::before { transform: none; }

  /* The five steps come up in order; each number pops once its photo is uncovered. */
  .js .fx-steps__list[data-reveal] .fx-step { opacity: 0; transform: translateY(var(--space-16)); transition: opacity .8s var(--ab-ease) calc(var(--i) * .12s), transform 1.1s var(--ab-ease) calc(var(--i) * .12s); }
  .js .fx-steps__list[data-reveal] .fx-step__num { transform: scale(0); transition: transform .6s cubic-bezier(.34, 1.56, .64, 1) calc(.6s + var(--i) * .12s); }
  .js .fx-steps__list[data-reveal].is-in :is(.fx-step, .fx-step__num) { opacity: 1; transform: none; }
  /* The player opens with a short rise, its film first. */
  .fx-reel[open] .fx-reel__stage { animation: fx-reel-in .4s var(--ab-ease) both; }
  .fx-reel[open]::backdrop { animation: fx-reel-fade .3s ease both; }

  /* The development path fills in round by round, each arrow sliding out to the next. */
  .js .fx-flow__list[data-reveal] .fx-flow__icon { opacity: 0; transform: scale(.4); transition: opacity .5s var(--ab-ease) calc(var(--i) * .15s), transform .7s cubic-bezier(.34, 1.56, .64, 1) calc(var(--i) * .15s); }
  .js .fx-flow__list[data-reveal] .fx-flow__arrow { opacity: 0; transform: translateX(calc(var(--space-4) * -1)); transition: opacity .5s var(--ab-ease) calc(.12s + var(--i) * .15s), transform .6s var(--ab-ease) calc(.12s + var(--i) * .15s); }
  .js .fx-flow__list[data-reveal] .fx-flow__name { opacity: 0; transform: translateY(var(--space-3)); transition: opacity .6s var(--ab-ease) calc(.1s + var(--i) * .15s), transform .7s var(--ab-ease) calc(.1s + var(--i) * .15s); }
  .js .fx-flow__list[data-reveal].is-in :is(.fx-flow__icon, .fx-flow__arrow, .fx-flow__name) { opacity: 1; transform: none; }

  .js .fx-rd-card[data-reveal] { opacity: 0; transform: translateY(var(--space-16)); transition: opacity .9s var(--ab-ease) var(--d, 0s), transform 1.1s var(--ab-ease) var(--d, 0s); }
  .js .fx-rd-card[data-reveal].is-in { opacity: 1; transform: none; }

  /* The three photos land on each other: from the upper left, the right, then below. */
  .js .fx-ethic__cluster[data-reveal] .fx-ethic__photo { opacity: 0; transition: opacity .9s var(--ab-ease) var(--d, 0s), transform 1.3s var(--ab-ease) var(--d, 0s); }
  .js .fx-ethic__cluster[data-reveal] .fx-ethic__photo--a { transform: translate(calc(var(--space-12) * -1), calc(var(--space-12) * -1)); }
  .js .fx-ethic__cluster[data-reveal] .fx-ethic__photo--b { transform: translateX(var(--space-16)); }
  .js .fx-ethic__cluster[data-reveal] .fx-ethic__photo--c { transform: translateY(var(--space-16)); }
  .js .fx-ethic__cluster[data-reveal].is-in .fx-ethic__photo { opacity: 1; transform: none; }

  .js .fx-contact__title[data-reveal] { opacity: 0; transform: translateY(var(--space-8)); transition: opacity .9s var(--ab-ease), transform 1.1s var(--ab-ease); }
  .js .fx-contact__title[data-reveal].is-in { opacity: 1; transform: none; }
}
/* Under the pointer: photos lean in a little, as on About. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  :is(.fx-step__media, .fx-rd-card__media, .fx-ethic__photo) img { transition: scale 1.8s var(--ab-ease), transform 1s var(--ab-ease); will-change: transform; }
  :is(.fx-step:hover .fx-step__media, .fx-rd-card:hover .fx-rd-card__media, .fx-ethic__photo:hover) img { transform: scale(1.06); }
}
/* The header lies on the banner film (comment cmvjdup2sdtqa: "background nó lớp gradient đen mờ dần"): see-through over a
   black wash that fades down past it, white menu and logo; solid white again once the page scrolls, under the pointer or
   holding focus (hub.css + js/main.js headerOver, as on the product-group pages). Desktop only, as the mechanism is. */
@media (min-width: 1025px) {
  [data-header-over] .fx-banner { margin-top: calc(-1 * var(--header-over-h)); }
  [data-header-over] .fx-banner__media { height: calc(max(35.19vw, 240px) + var(--header-over-h)); }
  /* The wash is the banner's own top edge (the header's ::before and ::after are taken: its sliding gold bar and its scroll
     progress line, sections.css), from under the menu row down past it, over the photo and the film. */
  [data-header-over] .fx-banner::before { content: ""; position: absolute; left: 0; right: 0; top: 0; z-index: 1; height: calc(var(--header-over-h) + var(--space-24)); background: linear-gradient(color-mix(in srgb, var(--color-black) 85%, transparent), color-mix(in srgb, var(--color-black) 55%, transparent) 43%, transparent); pointer-events: none; }
  [data-header-over]:has(.fx-banner) .site-header:not(.is-solid, :hover, :focus-within) :is(.menutab, .lang__current) { color: var(--color-white); }
  [data-header-over]:has(.fx-banner) .site-header:not(.is-solid, :hover, :focus-within) :is(.lang__globe, .lang__chev, .icon-btn img) { filter: brightness(0) invert(1); }
  [data-header-over]:has(.fx-banner) .site-header:not(.is-solid, :hover, :focus-within) .navbar__logo img { content: url("../assets/images/footer-logo.png"); }
}
/* Scroll scenes, desktop: the banner drifts down behind the page; the three photos float at their own depths. No listeners. */
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .fx-banner__media { animation: ab-drift-down linear both; animation-timeline: view(); animation-range: exit 0% exit 100%; }
    .fx-ethic__photo { animation: fx-float linear both; animation-timeline: view(); animation-range: cover 0% cover 100%; }
    .fx-ethic__photo--a { --fx-depth: var(--space-8); }
    .fx-ethic__photo--b { --fx-depth: var(--space-16); }
    .fx-ethic__photo--c { --fx-depth: var(--space-12); }
  }
}
@keyframes fx-ring { from { opacity: .7; scale: 1; } to { opacity: 0; scale: 1.4; } }
@keyframes fx-reel-in { from { opacity: 0; transform: translateY(var(--space-6)) scale(.97); } }
@keyframes fx-reel-fade { from { opacity: 0; } }
@keyframes fx-float { from { translate: 0 var(--fx-depth, 0); } to { translate: 0 calc(var(--fx-depth, 0) * -1); } }

/* ============================================================
   Narrower screens
   ============================================================ */
@media (max-width: 1279px) {
  .fx-steps__list { gap: var(--space-6); }
  .fx-rd-cards { gap: var(--space-8); }
  .fx-rd-card__name { font-size: var(--h4-size); line-height: var(--h4-lh); }
  .fx-flow__name { min-height: calc(var(--body-lh) * 2); font-size: var(--body-size); line-height: var(--body-lh); }
  .fx-contact__grid { grid-template-columns: minmax(0, 575fr) minmax(0, 96fr) minmax(0, 690fr); padding-left: 0; }
}
/* The steps title wraps below 1280: its bar then runs under each line, as wide as the words (About's rule for its long
   title; About already does this for every title on a phone). */
@media (min-width: 768px) and (max-width: 1279px) {
  .fx-steps .ab-title__t { display: inline; padding: 0; overflow: visible; }
  .fx-steps .ab-title__t::before { content: none; }
  .fx-steps .ab-title__in { display: inline; padding-inline: .34em; white-space: normal; background: linear-gradient(var(--color-primary), var(--color-primary)) 0 100% / 100% .45em no-repeat; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
}
@media (min-width: 768px) and (max-width: 1279px) and (prefers-reduced-motion: no-preference) {
  .js .fx-steps .ab-title[data-reveal] { opacity: 0; transform: translateY(var(--space-6)); transition: opacity .8s var(--ab-ease), transform .9s var(--ab-ease); }
  .js .fx-steps .ab-title[data-reveal].is-in { opacity: 1; transform: none; }
  .js .fx-steps .ab-title[data-reveal] .ab-title__in { transform: none; }
}
@media (min-width: 1025px) and (max-width: 1439px) {
  /* "Ethical Sourced" keeps to one line: the words take a little more of the row. */
  .fx-ethic__grid { grid-template-columns: minmax(0, 700fr) minmax(0, 800fr); }
  .fx-ethic__text { padding-right: var(--space-3); }
}
@media (max-width: 1024px) {
  .fx-eco, .fx-steps { padding-block: var(--space-12); }
  .fx-eco__grid, .fx-ethic__grid { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-8); }
  /* Steps three over two, the two centred. */
  .fx-steps__list { grid-template-columns: repeat(6, minmax(0, 1fr)); row-gap: var(--space-12); }
  .fx-step { grid-column: span 2; }
  .fx-step:nth-child(4) { grid-column: 2 / span 2; }
  /* The path four over three; the arrow at the end of the first row goes, the row turns instead. */
  .fx-flow__list { grid-template-columns: repeat(8, minmax(0, 1fr)); row-gap: var(--space-8); width: calc(100% - 2 * var(--space-6)); }
  .fx-flow__step { grid-column: span 2; }
  .fx-flow__step:nth-child(5) { grid-column: 2 / span 2; }
  .fx-flow__step:nth-child(4) .fx-flow__arrow { display: none; }
  /* R&D cards: one a row, photo beside its story. */
  .fx-rd-cards { grid-template-columns: minmax(0, 1fr); }
  .fx-rd-card { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .fx-rd-card__media { aspect-ratio: auto; }
  .fx-latest, .fx-ethic { padding-block: var(--space-16); }
  .fx-ethic__text { padding: 0; }
  .fx-contact { padding: var(--space-16) 0; }
  .fx-contact__grid { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-12); }
  .fx-contact .ab-form { grid-column: 1; }
}
@media (max-width: 767px) {
  .fx-eco .ab-justify p { text-align: left; }
  .fx-eco .ab-copy { line-height: 1.5; } /* the copy is 16px here; the desktop 32px leading would leave it gappy */
  .fx-eco__list { font-size: var(--body-size); line-height: var(--body-lh); }
  /* The steps title keeps to two lines on a phone (comment cmvjdx4qf0zun: four lines at 32px). */
  .fx-steps .ab-title { font-size: var(--h5-size); line-height: var(--h5-lh); }
  /* Steps one under another, each upright film the width of the screen (comment cmvjev1do11c7: "dàn dọc layout"), 64px
     apart so one step clearly ends before the next begins (comment cmvko35wr39ds: "Tăng gap … hơi sát"). */
  .fx-steps__list { grid-template-columns: minmax(0, 1fr); gap: var(--space-16); }
  .fx-step, .fx-step:nth-child(4), .fx-step:nth-child(5) { grid-column: auto; }
  .fx-step__name { min-height: 0; } /* one column: no row to line up */
  .fx-step__num { margin-top: var(--space-4); }
  .fx-rd__lead { font-size: var(--h6-size); line-height: var(--h6-lh); }
  /* The path runs down the screen: round, then its name; the arrow points down to the next. */
  .fx-flow { padding-block: var(--space-8); }
  .fx-flow__list { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-8); }
  .fx-flow__step, .fx-flow__step:nth-child(5) { display: grid; grid-column: auto; grid-template-columns: var(--space-16) minmax(0, 1fr); column-gap: var(--space-4); align-items: center; text-align: left; }
  .fx-flow__icon { width: var(--space-16); height: var(--space-16); }
  .fx-flow__icon { --fx-icon-k: 1.185px; } /* the icons shrink with their round, 64 of 96 */
  .fx-flow__name br { display: none; } /* beside the round the names keep to one line */
  .fx-flow__name { justify-content: flex-start; min-height: 0; margin-top: 0; font-size: var(--h6-size); line-height: var(--h6-lh); } /* 20px, as on desktop (comment cmvjdy3a56hku: "font chữ nhỏ quá") */
  .fx-flow__arrow, .fx-flow__step:nth-child(4) .fx-flow__arrow { display: block; left: var(--space-8); top: calc(100% + var(--space-1)); width: var(--space-6); height: var(--space-6); translate: -50% 0; rotate: 90deg; }
  .fx-flow__step:last-child .fx-flow__arrow { display: none; }
  .fx-rd-card { display: flex; }
  .fx-rd-card__media { aspect-ratio: var(--ty-le-tin); }
  .fx-ethic__copy { font-size: var(--h6-size); line-height: var(--h6-lh); }
  .fx-contact__sub { font-size: var(--h5-size); line-height: var(--h5-lh); }
  .fx-need + .fx-need { margin-top: var(--space-8); }
  .fx-need p { font-size: var(--body-size); line-height: var(--body-lh); }
}
