/* ==========================================================================
   QM Candle Machinery — design system
   Brand: #0040A0 (blue, from logo) / #E01018 (red, from logo flame)
   No external font or CDN requests: everything ships from this origin.
   ========================================================================== */

:root {
  --navy-900: #06183c;
  --navy-800: #0a2354;
  --navy-700: #0e2f6e;
  --blue-600: #0040a0;
  --blue-500: #0f55c4;
  --blue-300: #7ea6e4;
  --blue-100: #e7eefa;
  --blue-050: #f2f6fd;
  --red-600: #e01018;
  --red-700: #b60c13;

  --ink: #14181f;
  --ink-2: #37404f;
  --muted: #5c6879;
  --line: #dde3ec;
  --line-2: #eef1f6;
  --bg: #ffffff;
  --bg-soft: #f4f6f9;

  --radius: 10px;
  --radius-lg: 16px;
  --shadow-1: 0 1px 2px rgb(6 24 60 / 6%), 0 2px 8px rgb(6 24 60 / 5%);
  --shadow-2: 0 6px 16px rgb(6 24 60 / 10%), 0 18px 40px rgb(6 24 60 / 9%);

  --container: 1240px;
  --gutter: clamp(1rem, 4vw, 2.5rem);
  --section-y: clamp(3.5rem, 8vw, 6.5rem);
  --header-h: 68px;

  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI",
    system-ui, Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif,
    "Apple Color Emoji", "Segoe UI Emoji";
  --sans-cjk: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", "Source Han Sans SC",
    sans-serif;

  /* --- type scale ----------------------------------------------------------
     One ramp, ~1.25 between text sizes and opening up across the display sizes,
     so the jump from a heading to its body copy is a decision rather than an
     accident. Every size in this stylesheet comes from here: ad-hoc values are
     how the uppercase section kicker ended up LARGER than the body text it was
     supposed to be labelling. Fluid sizes interpolate between a 360px phone and
     a 1440px desktop. */
  --fs-3xs: 0.72rem;                                    /* 11.5 — tags        */
  --fs-2xs: 0.78rem;                                    /* 12.5 — kickers     */
  --fs-xs:  0.845rem;                                   /* 13.5 — meta        */
  --fs-sm:  0.925rem;                                   /* 14.8 — dense UI    */
  --fs-md:  clamp(0.95rem, 0.93rem + 0.1vw, 1rem);      /* nav, buttons       */
  --fs-base: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);  /* body               */
  --fs-lg:  clamp(1.06rem, 1.01rem + 0.24vw, 1.2rem);   /* lede, card titles  */
  --fs-xl:  clamp(1.18rem, 1.08rem + 0.5vw, 1.45rem);   /* block titles       */
  --fs-2xl: clamp(1.4rem, 1.2rem + 1vw, 2rem);          /* h3                 */
  /* h2's ceiling stays at 2.6rem deliberately. Several section headings on this
     site carry 150 characters of category copy rather than a title, and a
     bigger ceiling turns those into five lines of shouting. The floor is raised
     instead, so small screens gain the presence without the long ones paying. */
  --fs-3xl: clamp(1.75rem, 1.35rem + 1.9vw, 2.6rem);    /* h2                 */
  --fs-4xl: clamp(2.15rem, 1.55rem + 3vw, 3.9rem);      /* h1                 */

  /* Leading falls as size rises — the one rule that separates set type from
     defaulted type. A single line-height across the whole page leaves display
     sizes airy and body text cramped. */
  /* 1.12, not the 1.05–1.08 a desktop mock would stand: Roboto's content box is
     1.17em tall, so on the Android phones most of our target market reads on,
     tighter display leading puts a descender into the ascender below it. */
  --lh-display: 1.12;
  --lh-heading: 1.24;
  --lh-snug: 1.45;
  --lh-body: 1.6;

  /* Tracking is optical, so it is tied to size too: negative only where the
     size is large enough for default spacing to look loose, never on the small
     text where it costs legibility. */
  --track-display: -0.022em;
  --track-heading: -0.012em;
  --track-caps: 0.12em;
}

html[lang^="zh"] {
  --sans: var(--sans-cjk);
  /* Han glyphs carry no ascender/descender rhythm to lead the eye along a line,
     so CJK needs MORE leading than Latin at the same size, and negative
     tracking — which exists to close gaps between Latin letterforms — only
     makes a dense square glyph collide with its neighbour. */
  --lh-body: 1.8;
  --lh-heading: 1.4;
  --lh-display: 1.25;
  --track-display: 0;
  --track-heading: 0;
  --track-caps: 0.04em;
}

/* Vietnamese stacks a tone mark on top of a vowel that may already carry a
   diacritic, so it needs headroom English never asks for. */
html[lang="vi"] {
  --lh-display: 1.2;
  --lh-heading: 1.32;
}

/* --- reset ---------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: var(--lh-body);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  font-optical-sizing: auto;
  overflow-wrap: break-word;
}

img, video, svg { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }

h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  line-height: var(--lh-heading);
  font-weight: 700;
  letter-spacing: var(--track-heading);
  color: var(--navy-900);
  text-wrap: balance;
}

/* Display sizes only: heavier, tighter, and led closer than the section titles
   below them. */
h1, h2 {
  font-weight: 800;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
}

h1 { font-size: var(--fs-4xl); }
h2 { font-size: var(--fs-3xl); }
h3 { font-size: var(--fs-2xl); }
h4 { font-size: var(--fs-xl); }
/* 68 characters: past about 75 the eye loses the start of the next line, and
   under about 45 it jumps too often. `pretty` keeps a single word off the last
   line where the browser supports it. */
p  { margin: 0 0 1.1em; max-width: 68ch; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: var(--blue-600); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--red-600); }

:focus-visible {
  outline: 3px solid var(--blue-500);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -100px;
  z-index: 200;
  padding: 0.6rem 1rem;
  background: var(--navy-900);
  color: #fff;
  border-radius: var(--radius);
  text-decoration: none;
}
.skip-link:focus { top: 0.5rem; color: #fff; }

/* --- layout --------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container-narrow { max-width: 860px; }

.section { padding-block: var(--section-y); }
.section-soft { background: var(--bg-soft); }
.section-dark {
  background:
    radial-gradient(120% 110% at 12% 0%, #103070 0%, transparent 60%),
    var(--navy-900);
  color: #dce6f6;
}
.section-dark h1, .section-dark h2, .section-dark h3, .section-dark h4 { color: #fff; }
.section-dark a { color: var(--blue-300); }

.section-head { max-width: 760px; margin-bottom: clamp(2rem, 4vw, 3rem); }
.section-head.center { margin-inline: auto; text-align: center; }
/* `:not(.eyebrow)` is load-bearing, not tidiness: `.section-head p` outranks
   `.eyebrow`, so the kicker was being served the standfirst's 1.06em and coming
   out BIGGER than the body text it labels — with the 0.14em tracking then
   computed against that inflated size. */
.section-head p:not(.eyebrow) { color: var(--muted); font-size: var(--fs-lg); }
.section-dark .section-head p:not(.eyebrow) { color: #a9bcda; }

.eyebrow {
  display: inline-block;
  margin: 0 0 0.85rem;
  font-size: var(--fs-2xs);
  line-height: var(--lh-snug);
  font-weight: 700;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--blue-600);
}
.section-dark .eyebrow { color: var(--blue-300); }
.eyebrow-red { color: var(--red-600); }

.stack > * + * { margin-top: 1.1em; }

/* Company intro. It sits above six image sheets, and at body size on an
   86-character line it read as a caption beside them rather than as the thing
   the page opens with. Raised to standfirst scale AND cut to a ~65-character
   measure: enlarging alone would have pushed the line past 90 characters, which
   buys presence by spending legibility. Leading drops as size rises.

   It lives in the standard .container, not .container-narrow: narrow indented
   it 62px from the image sheets, the section headings and the page h1, so it
   was the one block on the page off the common left edge.

   Wide screens set it in two columns so the block spans the full image width
   with both edges flush. That is the only way to fill that width without the
   measure blowing out — one column across 1160px would run ~100 characters a
   line. Two paragraphs, two columns: the split falls on the existing paragraph
   break, so nothing has to be re-flowed or hyphenated. */
.page-intro {
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  max-width: 33em;
  color: var(--ink);
  text-wrap: pretty;
}
/* Below this the two columns would each fall under ~40 characters, which reads
   worse than a single column — so the block stays one column and keeps its
   measure cap. Not a device breakpoint: it is where the content stops fitting. */
@media (min-width: 1080px) {
  .page-intro {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: start;
    gap: clamp(2rem, 3.5vw, 3.25rem);
    max-width: none;
  }
  /* .stack's flow spacing would otherwise push the second column down. */
  .page-intro > * + * { margin-top: 0; }
}
/* --lh-snug has no CJK override (unlike --lh-body/-heading/-display), so Han
   at this size would be set at Latin leading — the very thing the html[lang^=zh]
   block warns against. Corrected here rather than on the token: --lh-snug is
   also feeding the hero lede and every card, and widening those is a separate
   decision. */
html[lang^="zh"] .page-intro { line-height: 1.7; }

/* Optical: the kicker is set against a much larger first line now, so it needs
   more clearance under it than the 1.1em default at kicker size would give. */
.page-intro-kicker { margin-bottom: 1.4rem; }

/* --- buttons -------------------------------------------------------------- */

.btn {
  --btn-bg: var(--blue-600);
  --btn-fg: #fff;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  padding: 0.85em 1.6em;
  min-height: 48px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 700;
  font-size: var(--fs-md);
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
/* An inline SVG carrying only a viewBox has no intrinsic size, so it falls back
   to the replaced-element default — 69px here, which inflated the button and
   squeezed its label onto three lines. Sizing it in em keeps the icon tied to
   the label whatever the button's size class. */
.btn svg { width: 1.35em; height: 1.35em; flex: none; }
.btn:hover {
  color: var(--btn-fg);
  transform: translateY(-1px);
  box-shadow: var(--shadow-2);
}
.btn:active { transform: none; }
.btn-red { --btn-bg: var(--red-600); }
.btn-red:hover { --btn-bg: var(--red-700); }
/* The logo blue, matching the contact bar — one WhatsApp colour across the
   site rather than green here and blue there. */
.btn-wa { --btn-bg: var(--blue-600); }
.btn-wa:hover { --btn-bg: var(--navy-700); }
.btn-ghost {
  --btn-bg: transparent;
  --btn-fg: var(--navy-900);
  border-color: var(--line);
  background: #fff;
}
.btn-ghost:hover { border-color: var(--blue-600); }
.btn-on-dark {
  --btn-bg: rgb(255 255 255 / 10%);
  --btn-fg: #fff;
  border-color: rgb(255 255 255 / 35%);
  backdrop-filter: blur(6px);
}
.btn-on-dark:hover { --btn-bg: rgb(255 255 255 / 20%); }
.btn-lg { padding: 1.05em 2em; font-size: var(--fs-base); }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  margin-top: 1.75rem;
}

/* --- header --------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  /* Deliberately opaque with no backdrop-filter: a filtered header becomes the
     containing block for the position:fixed mobile drawer nested inside it,
     which collapses the drawer to the height of the header. */
  background: #fff;
  border-bottom: 1px solid var(--line);
}
.header-inner {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: var(--header-h);
}

.brand {
  display: flex;
  align-items: center;
  /* The masthead is a flex row and the logo was the item that gave way when it
     ran out of room — at 901px in the longest language it shrank to zero and
     the site had no visible mark at all. Nothing about the logo is negotiable. */
  flex: none;
  margin-right: auto;
  text-decoration: none;
  color: var(--navy-900);
}
/* Sized by HEIGHT against the trimmed mark, not by a square box: the mark is
   portrait (~0.71), so height is what the eye reads against the header, and
   the square canvas it used to be cut from wasted 40% of its own width on
   margin. 46px inside a 68px header keeps 11px of air above and below. */
.brand img {
  height: clamp(40px, 4.4vw, 46px);
  width: auto;
  flex-shrink: 0;
}
/* The masthead is the logo alone, at every width. The text stays in the markup
   and out of the layout: the logo is alt="" decorative, so this is the only
   accessible name the link home has — display:none would leave a screen reader
   announcing an unnamed link, and would strip the company name from the top of
   the document outline. */
.brand-text {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.nav-toggle {
  display: none;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  padding: 0.5rem 0.75rem;
  min-height: 44px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  font-weight: 700;
  font-size: var(--fs-sm);
  cursor: pointer;
}
.nav-toggle svg { width: 18px; height: 18px; }

/* `> ul` throughout, not `ul`: the language panel is a <ul> inside this nav as
   well, and the descendant selector was laying its options out as a nav row. */
.site-nav > ul {
  display: flex;
  align-items: center;
  gap: clamp(0.4rem, 1.1vw, 1.1rem);
  margin: 0;
  padding: 0;
  list-style: none;
}
.site-nav > ul a {
  display: block;
  padding: 0.5rem 0.35rem;
  color: var(--ink-2);
  font-weight: 600;
  font-size: var(--fs-sm);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
}
.site-nav > ul a:hover { color: var(--blue-600); }
.site-nav > ul a[aria-current="page"] {
  color: var(--blue-600);
  border-bottom-color: var(--red-600);
}

/* --- language switcher ----------------------------------------------------
   A <details> disclosure: one control naming the CURRENT language, opening a
   panel of all of them. Four codes laid out inline was the old shape, and it
   scaled badly in both directions — it ate the header width the brand needed,
   and "VI" is a code the reader has to decode before they can find their own
   language. It lives inside the nav, so on a phone it is simply the first thing
   in the drawer. */

.lang-switch { position: relative; flex-shrink: 0; }
.lang-switch summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.45rem 0.6rem;
  min-height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;                 /* Firefox's default marker */
}
.lang-switch summary::-webkit-details-marker { display: none; }
.lang-switch summary:hover { border-color: var(--blue-600); color: var(--blue-600); }
.lang-globe { width: 17px; height: 17px; flex: none; }
.lang-chevron { width: 14px; height: 14px; flex: none; transition: transform 0.15s ease; }
.lang-switch[open] > summary .lang-chevron { transform: rotate(180deg); }

.lang-switch ul {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 110;
  min-width: 190px;
  margin: 0;
  padding: 0.35rem;
  list-style: none;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-2);
}
.lang-switch ul a {
  display: block;
  padding: 0.5rem 0.7rem;
  border-radius: 6px;
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.lang-switch ul a:hover { background: var(--blue-050); color: var(--blue-600); }
.lang-switch ul a[aria-current="true"] { background: var(--blue-600); color: #fff; }

/* Kept in step with the matchMedia query in site.js, which closes the drawer on
   the way up past this width.
   Raised from 900 to 1023 when Cases became a sixth nav item. Measured, not
   guessed: six items, the switcher and the button need 1129px of viewport in
   Indonesian to sit on one row untruncated, and 1010px even with the switcher
   squeezed to its globe. Below that the row is a hamburger, which is honest —
   the alternative is shaving type until something breaks in one language. */
@media (max-width: 1023px) {
  .nav-toggle { display: inline-flex; order: 3; }
  .site-nav {
    position: fixed;
    inset: var(--header-h) 0 0;
    z-index: 99;
    padding: 1.25rem var(--gutter) 3rem;
    background: #fff;
    overflow-y: auto;
    transform: translateY(-8px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
  }
  .site-nav[data-open="true"] { opacity: 1; visibility: visible; transform: none; }
  .site-nav > ul { display: block; }
  .site-nav > ul li + li { border-top: 1px solid var(--line-2); }
  .site-nav > ul a {
    padding: 1rem 0.25rem;
    font-size: var(--fs-lg);
    font-weight: 700;
    border-bottom: 0;
  }
  .site-nav > ul a[aria-current="page"] { border-left: 3px solid var(--red-600); padding-left: 0.75rem; }
  .site-nav .nav-cta { display: block; margin-top: 1.5rem; }
  .site-nav .nav-cta .btn { width: 100%; }

  /* In the drawer the switcher is not a dropdown at all: it is a single row of
     languages across the top of the panel, scrolled sideways when it does not
     fit — the shape both reference sites use. Not wrapped: a strip that wraps
     reads as a block of buttons, and a block of buttons at the top of a menu
     competes with the navigation instead of sitting under it. */
  .lang-switch { margin-bottom: 1.1rem; padding-bottom: 1.1rem; border-bottom: 1px solid var(--line); }
  .lang-switch summary { justify-content: space-between; min-height: 48px; }
  .lang-switch summary .lang-chevron { margin-left: auto; }
  /* Hidden only once the script has confirmed it can open the panel itself.
     Without JS the summary stays, and the strip is one tap away rather than
     unreachable. */
  .lang-switch.is-strip > summary { display: none; }
  .lang-switch ul {
    position: static;
    display: flex;
    flex-wrap: nowrap;
    gap: 0.4rem;
    min-width: 0;
    margin-top: 0.5rem;
    padding: 0;
    border: 0;
    box-shadow: none;
    overflow-x: auto;
    overscroll-behavior-x: contain;   /* no back-swipe from the end of the row */
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .lang-switch.is-strip ul { margin-top: 0; }
  .lang-switch ul::-webkit-scrollbar { display: none; }
  /* Bleeds to the drawer's edges: a strip that stops inside the gutter looks
     finished, and nobody swipes something that looks finished. */
  .lang-switch ul {
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
  }
  .lang-switch ul li { scroll-snap-align: start; flex: none; }
  .lang-switch ul a {
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--line);
    border-radius: 100px;
    font-size: var(--fs-sm);
    white-space: nowrap;
  }
}
@media (min-width: 1024px) {
  .site-nav { display: flex; align-items: center; gap: clamp(0.6rem, 1.4vw, 1.4rem); }
  /* The switcher is FIRST in the markup so it lands at the top of the drawer on
     a phone; on a wide screen it belongs after the nav items instead. */
  .site-nav .lang-switch { order: 2; }
  /* Was display:none, which left the masthead with no call to action at all on
     a desktop — the button existed but only ever rendered inside the phone
     drawer. It sits last, after the switcher. */
  .site-nav .nav-cta { display: block; order: 3; }
  .site-nav .nav-cta .btn {
    padding: 0.5rem 1rem;
    font-size: var(--fs-sm);
    white-space: nowrap;
  }
  /* Adding the button made the masthead wide enough to overflow in the longest
     language: at 901px, Indonesian needs 841px of nav for 824px of room, and
     the brand was the flex item that gave way — the logo shrank to nothing.
     Rather than tune numbers per language, the nav items and the button are
     fixed and the language switcher is the one thing allowed to give, losing
     the tail of its own name to an ellipsis. The dropdown still lists every
     language in full, and a language added later inherits the behaviour. */
  .site-nav > ul, .site-nav .nav-cta { flex: none; }
  /* min-width on a flex item defaults to `auto`, which is a floor at its own
     content size — so the nav sat at its full width and hung off the right
     edge no matter what its children were allowed to do. This is the line that
     actually lets the row shrink. */
  .site-nav { min-width: 0; }
  /* Overrides the flex-shrink:0 the switcher carries at every other width. */
  .site-nav .lang-switch { flex: 0 1 auto; min-width: 0; }
  .site-nav .lang-switch summary { min-width: 0; }
  .site-nav .lang-current {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
/* Between 1024 and ~1200 there is no slack to spend on gutters. */
/* The narrow end of the desktop layout, where seven nav items, a language
   switcher and a call to action have to share one row.

   A seventh item (Guides) is what pushed this over. The ellipsis rule above was
   sized for six: measured at 1024px in Indonesian the row wanted 900px of the
   881px it had, so "Bahasa Indonesia" lost 124 of its 128 pixels and then the
   two icons were crushed from 18px to 3.5px each — the switcher rendered as an
   empty grey box. Three changes, in the order they matter:

   1. Drop the language label on purpose and keep the globe. It is a language
      affordance in its own right, which is why the icon is a globe and not a
      flag, and <summary aria-label="Language: Bahasa Indonesia"> means the
      accessible name never depended on the visible text.
   2. Take the remaining space from the item gap, the item type and the button
      padding together, rather than shaving one of them to land exactly on the
      line — where the next language, or the next nav item, puts it straight
      back over. 0.85rem is still above the 14px floor used elsewhere for
      dense UI.
   3. Give the icons a shrink floor (below), so the switcher can never again be
      the thing that silently disappears.

   The boundary is 1239px, found by measuring rather than by picking a round
   number. It is higher than it looks like it should be: leaving this block
   restores the full type and gaps too, which costs more than the label gains,
   so at 1201px "Bahasa Indonesia" was clipped by 29px — worse than at 1180.
   It first renders whole at 1240px, which is also where .container stops
   growing, so no width above it is any roomier. */
@media (min-width: 1024px) and (max-width: 1239px) {
  .site-nav { gap: 0.6rem; }
  .site-nav > ul { gap: 0.4rem; }
  .site-nav > ul a { font-size: 0.85rem; }
  .site-nav .nav-cta .btn { padding-inline: 0.8rem; font-size: 0.85rem; }
  .site-nav .lang-current { display: none; }
  .site-nav .lang-switch summary { gap: 0.3rem; }
}
/* The switcher is the one flex item allowed to shrink, so whatever is inside it
   is what gets crushed when the row runs out of room. With the label hidden
   above, that became the icons: both SVGs collapsed from 18px to 3.5px and the
   control rendered as an empty grey box. They are the whole affordance now. */
.lang-switch summary > svg { flex: none; }
@media (max-width: 600px) {
  .nav-toggle span { display: none; }
  .nav-toggle { padding: 0.5rem 0.7rem; }
}

body[data-nav-open="true"] { overflow: hidden; }

/* --- mobile contact bar --------------------------------------------------- */

/* Two actions, pinned in the thumb zone. Before this both of them were behind
   the hamburger, which on a phone is three taps to reach the thing most of
   these visitors came to do. */
.contact-bar {
  position: fixed;
  inset: auto 0 0;
  z-index: 90;                       /* under the nav drawer (99) and modals */
  display: none;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: rgba(255, 255, 255, 0.22);   /* the hairline between the halves */
  padding-bottom: env(safe-area-inset-bottom);
  box-shadow: 0 -6px 20px rgba(6, 24, 60, 0.18);
}
.contact-bar a {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  min-height: 56px;                  /* comfortably over the 44px touch floor */
  padding: 0.8rem 0.5rem;
  color: #fff;
  font-size: var(--fs-sm);
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  text-decoration: none;
}
.contact-bar a:hover, .contact-bar a:focus-visible { color: #fff; }
.contact-bar svg { width: 1.25em; height: 1.25em; flex: none; }
/* The two logo inks: the blue of the mark's base and the red of its flame. The
   bar reads as part of the brand rather than as WhatsApp's own green, which is
   the trade — recognition of the channel for recognition of the company.
   Sampled from logo-mark-240.png at #0446a3 and #e5171e, which is within a
   couple of points of the palette tokens used here. Both clear AA against
   white text: 9.37:1 and 4.93:1. */
.contact-bar-wa { background: var(--blue-600); }
.contact-bar-quote { background: var(--red-600); }

@media (max-width: 1023px) {
  .contact-bar { display: grid; }
  /* So the last of the page, and the footer, clear the bar. */
  body { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }
}
/* Never over the drawer, the video modal or the 3D overlay. */
body[data-nav-open="true"] .contact-bar,
body.modal-open .contact-bar { display: none; }

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

.hero {
  position: relative;
  isolation: isolate;
  display: grid;
  align-items: center;
  min-height: clamp(480px, 74vh, 720px);
  padding-block: clamp(3rem, 8vw, 6rem);
  background: var(--navy-900);
  color: #fff;
  overflow: hidden;
}
.hero-media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.hero-media img, .hero-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}
/* The reset gives every video display:block, which otherwise overrides the
   HTML hidden state. The factory-film modal sets hidden on this video so native
   video compositor layers cannot bleed through the overlay. */
.hero-media video[hidden] { display: none; }
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(100deg, rgb(6 24 60 / 92%) 0%, rgb(6 24 60 / 78%) 45%, rgb(6 24 60 / 45%) 100%),
    linear-gradient(0deg, rgb(6 24 60 / 55%), rgb(6 24 60 / 10%));
}
.hero h1 { color: #fff; max-width: 17ch; }
.hero-lede {
  max-width: 54ch;
  font-size: var(--fs-xl);
  line-height: var(--lh-snug);
  color: #cddcf2;
}
.hero-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
  margin-top: 2.25rem;
  padding-top: 1.4rem;
  border-top: 1px solid rgb(255 255 255 / 18%);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: #b9cbe8;
}
.hero-badges span { display: inline-flex; align-items: center; gap: 0.45rem; }
.hero-badges span::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--red-600);
}

.page-hero {
  padding-block: clamp(2.5rem, 6vw, 4.25rem);
  background:
    radial-gradient(90% 120% at 8% 0%, #123472 0%, transparent 60%),
    var(--navy-900);
  color: #cddcf2;
}
.page-hero h1 { color: #fff; margin-bottom: 0.35em; }
.page-hero p { max-width: 62ch; }
/* Quieter than the lede above it — it answers "is this still true?", which is
   a question the reader only asks after reading the thing. #9db4d6 on the
   hero's #06183c is 6.6:1, so quiet does not mean unreadable. */
.page-updated {
  margin-top: 1.1rem;
  font-size: 0.8125rem;
  color: #9db4d6;
}
.page-updated time { font-variant-numeric: tabular-nums; }

.breadcrumb {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1.1rem;
  padding: 0;
  list-style: none;
  font-size: var(--fs-xs);
  color: #93a8c9;
}
.breadcrumb a { color: #b9cbe8; text-decoration: none; }
.breadcrumb a:hover { color: #fff; text-decoration: underline; }
.breadcrumb li + li::before { content: "/"; margin-right: 0.4rem; opacity: 0.5; }

/* --- stats ---------------------------------------------------------------- */

.stat-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.stat-bar div { padding: 1.5rem 1.25rem; background: #fff; text-align: center; }
.stat-bar b {
  display: block;
  font-size: clamp(1.7rem, 1.3rem + 1.6vw, 2.4rem);
  font-weight: 800;
  letter-spacing: var(--track-display);
  font-variant-numeric: tabular-nums;
  color: var(--blue-600);
}
.stat-bar span { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }

.stat-bar-dark { background: rgb(255 255 255 / 12%); border-color: rgb(255 255 255 / 12%); }
.stat-bar-dark div { background: var(--navy-800); }
.stat-bar-dark b { color: #fff; }
.stat-bar-dark span { color: #a9bcda; }

/* --- cards / grids -------------------------------------------------------- */

.grid { display: grid; gap: clamp(1rem, 2.2vw, 1.75rem); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); }
/* auto-FILL, not auto-fit: a category holding one machine must show a normal
   card, not a single tile stretched across the whole row. */
.grid-3 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
/* The four machines you can take apart: 1 → 2 → 4 columns, never 3. Fixed
   counts rather than auto-fit, because every three-column width orphans the
   fourth card on a row of its own directly under a heading that says "four". */
.grid-viewers { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 560px) { .grid-viewers { grid-template-columns: minmax(0, 1fr); } }
@media (min-width: 1040px) { .grid-viewers { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.card {
  display: flex;
  flex-direction: column;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
.card-link { text-decoration: none; color: inherit; }
.card-link:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-2);
  border-color: var(--blue-300);
  color: inherit;
}
/* Every product tile is the same square regardless of whether the photograph is
   portrait, landscape or square, so the grid reads as one set.
   The <img> must be taken out of flow: `aspect-ratio` only sets a *preferred*
   size, and an in-flow image at its intrinsic ratio overrides it — which is why
   a 4:3 box still came out portrait-tall for the portrait shots. */
.card-media {
  position: relative;
  aspect-ratio: 1 / 1;
  background: linear-gradient(180deg, #fff 0%, #f4f7fb 100%);
  border-bottom: 1px solid var(--line-2);
}
.card-media picture { display: block; }
.card-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: clamp(0.9rem, 3.5%, 1.6rem);
}
.card-body { padding: 1.25rem 1.35rem 1.5rem; display: flex; flex-direction: column; flex: 1; }
.card-body h3 { font-size: var(--fs-lg); line-height: var(--lh-heading); margin-bottom: 0.4rem; }
.card-body p { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--muted); margin-bottom: 1rem; }
.card-more {
  margin-top: auto;
  font-size: var(--fs-sm);
  font-weight: 700;
  color: var(--blue-600);
}
.card-link:hover .card-more { color: var(--red-600); }
.card-more::after { content: " →"; }

.tag {
  display: inline-block;
  padding: 0.28em 0.7em;
  border-radius: 100px;
  font-size: var(--fs-3xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: var(--blue-100);
  color: var(--blue-600);
}
.tag-red { background: #fde8e9; color: var(--red-700); }
.card-media .tag { position: absolute; top: 0.85rem; left: 0.85rem; z-index: 1; }

/* --- feature blocks ------------------------------------------------------- */

.feature {
  padding: 1.6rem 1.5rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.feature h3 { font-size: var(--fs-xl); }
.feature p { font-size: var(--fs-sm); line-height: var(--lh-snug); color: var(--muted); margin: 0; }
.feature-icon {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  margin-bottom: 1rem;
  border-radius: 12px;
  background: var(--blue-100);
  color: var(--blue-600);
}
.feature-icon svg { width: 24px; height: 24px; }
.section-dark .feature {
  background: rgb(255 255 255 / 5%);
  border-color: rgb(255 255 255 / 12%);
}
.section-dark .feature p { color: #a9bcda; }
.section-dark .feature-icon { background: rgb(255 255 255 / 10%); color: var(--blue-300); }

.split {
  display: grid;
  gap: clamp(1.75rem, 5vw, 4rem);
  align-items: center;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 380px), 1fr));
}
.split-media img { border-radius: var(--radius-lg); border: 1px solid var(--line); }
.split-media.plain img { border: 0; }
.split.reverse .split-media { order: -1; }
@media (max-width: 860px) {
  .split.reverse .split-media { order: 0; }
}

/* --- checklist ------------------------------------------------------------ */

.checklist { margin: 0; padding: 0; list-style: none; }
.checklist li {
  position: relative;
  padding: 0.55rem 0 0.55rem 2rem;
  border-bottom: 1px solid var(--line-2);
}
.checklist li:last-child { border-bottom: 0; }
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.05em;
  width: 13px;
  height: 8px;
  border-left: 2.5px solid var(--blue-600);
  border-bottom: 2.5px solid var(--blue-600);
  transform: rotate(-45deg) translateY(-2px);
}
.section-dark .checklist li { border-color: rgb(255 255 255 / 12%); }
.section-dark .checklist li::before { border-color: var(--blue-300); }

/* --- process rail --------------------------------------------------------- */

.process {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 155px), 1fr));
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
/* Cell separators are drawn inside the cells, not as a gap over a coloured
   backdrop: with an odd item count (7 steps) the leftover grid area would
   otherwise show up as a grey block. The container clips the trailing edges. */
.process > div {
  position: relative;
  padding: 1.4rem 1.1rem;
  background: #fff;
  box-shadow: inset -1px 0 0 var(--line), inset 0 -1px 0 var(--line);
}
.process b {
  display: block;
  margin-bottom: 0.35rem;
  font-size: var(--fs-sm);
  color: var(--navy-900);
}
.process span { font-size: var(--fs-xs); line-height: var(--lh-snug); color: var(--muted); }
.process i {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  margin-bottom: 0.7rem;
  border-radius: 50%;
  background: var(--blue-600);
  color: #fff;
  font-size: var(--fs-2xs);
  font-weight: 800;
  font-style: normal;
}

/* --- spec table ----------------------------------------------------------- */

.spec-table {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.spec-table th, .spec-table td {
  padding: 0.85rem 1.1rem;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line-2);
}
.spec-table th {
  width: 40%;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--bg-soft);
}
.spec-table tr:last-child th, .spec-table tr:last-child td { border-bottom: 0; }

@media (max-width: 560px) {
  .spec-table, .spec-table tbody, .spec-table tr, .spec-table th, .spec-table td {
    display: block;
    width: auto;
  }
  .spec-table tr { border-bottom: 1px solid var(--line); }
  .spec-table tr:last-child { border-bottom: 0; }
  .spec-table th { border-bottom: 0; padding-bottom: 0.2rem; background: none; font-size: var(--fs-xs); color: var(--muted); }
  .spec-table td { padding-top: 0; font-weight: 600; }
}

/* A table wider than the phone scrolls inside its own box rather than pushing
   the page sideways. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* Three columns of prose, not a two-column spec sheet: the row header should
   take what it needs, not 40% of the width. */
.spec-table-wide th { width: auto; white-space: nowrap; }
.spec-table-wide thead th {
  background: var(--bg-soft);
  font-size: var(--fs-xs);
  color: var(--muted);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.spec-table td.num { font-weight: 700; color: var(--blue-600); white-space: nowrap; }
@media (max-width: 560px) {
  /* Stacked, the column headings become three stray words above the first row. */
  .spec-table-wide thead { display: none; }
  .spec-table-wide th { white-space: normal; }
}

.lede-narrow {
  max-width: 68ch;
  margin: 0 0 1.5rem;
  color: var(--ink-2);
}

/* --- FAQ ------------------------------------------------------------------ */

/* <details> rather than scripted accordions: it opens without JavaScript, it is
   keyboard-operable for free, and the browser's own find-in-page can open it. */
.faq-list { border-top: 1px solid var(--line); }
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.15rem 0.25rem;
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: var(--lh-snug);
  color: var(--navy-900);
  cursor: pointer;
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover { color: var(--blue-600); }
.faq-item summary svg {
  width: 1.1rem;
  height: 1.1rem;
  flex: none;
  margin-left: auto;
  color: var(--blue-600);
  transition: transform 0.18s ease;
}
.faq-item[open] summary svg { transform: rotate(180deg); }
.faq-answer {
  padding: 0 0.25rem 1.35rem;
  max-width: 72ch;
  color: var(--ink-2);
}
.faq-answer p { margin: 0; }
@media (prefers-reduced-motion: reduce) {
  .faq-item summary svg { transition: none; }
}

/* --- checklist ------------------------------------------------------------ */

.check-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 72ch;
}
.check-list li {
  position: relative;
  padding: 0.7rem 0 0.7rem 1.9rem;
  border-bottom: 1px solid var(--line-2);
  color: var(--ink-2);
}
.check-list li:last-child { border-bottom: 0; }
.check-list li::before {
  content: "";
  position: absolute;
  left: 0.25rem;
  top: 1.25rem;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--red-600);
}

/* --- customer cases ------------------------------------------------------- */

/* All three films are portrait — they were shot on a phone on a factory floor,
   which is the only way footage like this ever exists. Three columns rather
   than the site's usual two: a portrait video in a half-width column is over a
   thousand pixels tall and pushes its own caption off the screen. */
.case-films {
  display: grid;
  gap: clamp(1.25rem, 2.5vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  align-items: start;
}
.case-film { margin: 0; }
.case-film figcaption {
  margin-top: 0.85rem;
  max-width: 46ch;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--muted);
}
.case-film-machine {
  display: block;
  margin-top: 0.5rem;
  font-weight: 700;
  text-decoration: none;
}
.case-film-machine:hover { text-decoration: underline; }

.case-stats {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1.5rem, 5vw, 3.5rem);
  margin: 0 0 1.75rem;
  padding: 0;
}
.case-stats b {
  display: block;
  font-size: clamp(2rem, 5vw, 2.75rem);
  line-height: 1.05;
  color: var(--blue-600);
  font-variant-numeric: tabular-nums;
}
.case-stats span { font-size: var(--fs-sm); color: var(--muted); }

/* The three pre-sale pages, linked from under every product's buy buttons. */
.buying-links { margin-top: 0.9rem; font-size: var(--fs-sm); }
.buying-links a { color: var(--blue-600); }

.note {
  padding: 0.9rem 1.1rem;
  border-left: 3px solid var(--blue-600);
  background: var(--blue-050);
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: var(--fs-sm);
  color: var(--ink-2);
}
.note p { margin: 0; }

/* --- product detail ------------------------------------------------------- */

.product-layout {
  display: grid;
  gap: clamp(1.75rem, 4vw, 3.25rem);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 940px) {
  .product-layout { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); align-items: start; }
}
.product-figure {
  position: relative;
  background: linear-gradient(180deg, #fff 0%, #eef2f8 100%);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: clamp(1rem, 3vw, 2rem);
}
.product-figure > picture img,
.product-figure > img { width: 100%; object-fit: contain; margin-inline: auto; }

/* Two detail shots under the main image, inside the same card. They are part of
   the figure so the whole gallery is one grid item and goes sticky together —
   as siblings they would scroll out from under a stuck main image. */
.product-details {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.6rem;
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--line);
}
.product-detail {
  display: block;
  border-radius: calc(var(--radius-lg) - 6px);
  overflow: hidden;
  background: #fff;
  border: 1px solid var(--line);
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.product-detail:hover { border-color: var(--red-600); transform: translateY(-2px); }
.product-detail:focus-visible { outline: 2px solid var(--red-600); outline-offset: 2px; }
/* Fixed ratio so two shots of different native ratios still line up. */
.product-detail img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  display: block;
}
@media (min-width: 940px) {
  .product-figure { position: sticky; top: calc(var(--header-h) + 1.5rem); }
  .product-figure > picture img,
  .product-figure > img { max-height: 62vh; }
  /* With a gallery below it the card must still fit between the sticky top and
     the bottom of the viewport, so the hero shot yields the thumbs' height. */
  .product-figure:has(.product-details) > picture img,
  .product-figure:has(.product-details) > img { max-height: 48vh; }
}

.highlight-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
  gap: 1px;
  margin: 1.5rem 0;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.highlight-row div { padding: 0.9rem 1rem; background: #fff; }
.highlight-row b {
  display: block;
  font-size: var(--fs-xl);
  font-weight: 800;
  letter-spacing: var(--track-display);
  font-variant-numeric: tabular-nums;
  color: var(--red-600);
}
.highlight-row span { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }

.subhead {
  margin: 2.5rem 0 1rem;
  padding-bottom: 0.6rem;
  border-bottom: 2px solid var(--line);
  font-size: var(--fs-xl);
}

/* --- testimonials --------------------------------------------------------- */

.review {
  display: flex;
  flex-direction: column;
  padding: 1.4rem 1.5rem;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}
.review-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
  font-size: var(--fs-xs);
}
.review-top b { font-weight: 700; }
.review-flag { font-size: var(--fs-lg); line-height: 1; }
.review-top time { margin-left: auto; color: var(--muted); font-size: var(--fs-2xs); }
.stars { color: #f5a623; letter-spacing: 0.08em; font-size: var(--fs-sm); }
.review blockquote { margin: 0.6rem 0 0; }  /* drop the UA's 40px side indent */
.review p { margin: 0; font-size: var(--fs-sm); color: var(--ink-2); }
.review-country { color: var(--muted); }

/* --- media blocks --------------------------------------------------------- */

.video-frame {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #000;
  border: 1px solid var(--line);
}
.section-dark .video-frame { border-color: rgb(255 255 255 / 15%); }
.video-frame video { width: 100%; height: auto; display: block; }
.video-frame.portrait { max-width: 380px; }

.sheet-figure {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #fff;
}
.sheet-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.sheet-figure img { width: 100%; }
.sheet-figure figcaption {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1rem;
  align-items: baseline;
  padding: 0.7rem 1.1rem;
  font-size: var(--fs-xs);
  color: var(--muted);
  border-top: 1px solid var(--line-2);
  background: var(--bg-soft);
}
/* Two sheets stacked in one section. */
.sheet-stack > .sheet-figure + .sheet-figure { margin-top: 1.4rem; }

/* A real-text sentence under artwork whose own copy is baked-in English pixels.
   Takes the whole first line of the caption row so the hint and the full-size
   link stay together on the line below it. */
.sheet-caption {
  flex: 1 1 100%;
  margin: 0;
  color: var(--ink);
  font-size: var(--fs-sm);
  line-height: 1.6;
}
.sheet-figure figcaption a { margin-left: auto; font-weight: 600; text-decoration: none; }
.sheet-figure figcaption a:hover { text-decoration: underline; }
.sheet-hint { display: none; }

/* Sheets whose text is set in columns become illegible when scaled to a phone.
   Below this width they keep a readable size and the figure scrolls sideways. */
@media (max-width: 760px) {
  .sheet-wide .sheet-scroll img {
    width: auto;
    max-width: none;
    min-width: 700px;
  }
  .sheet-wide .sheet-hint { display: inline; }
}

/* The text equivalent of a sheet. The artwork carries this copy already, but
   baked into the pixels in English only — so it cannot be translated, selected,
   found by search, or read aloud. This is the same information as real text.
   Deliberately quieter than the sheet: it supports the image, it is not a
   second headline. */
.sheet-notes { margin-top: clamp(1.25rem, 2.5vw, 1.9rem); }
.sheet-notes-lead {
  max-width: 68ch;
  margin: 0;
  font-size: var(--fs-lg);
  line-height: var(--lh-snug);
  color: var(--ink-2);
}
.sheet-notes-list {
  list-style: none;
  margin: clamp(1rem, 2vw, 1.4rem) 0 0;
  padding: 0;
  display: grid;
  gap: 1rem clamp(1.5rem, 3vw, 2.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.sheet-notes-list li {
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--muted);
}
.sheet-notes-list b {
  display: block;
  margin-bottom: 0.2rem;
  font-size: var(--fs-md);
  color: var(--ink);
}
/* Same reasoning as .page-intro: --lh-snug carries no CJK override. */
html[lang^="zh"] .sheet-notes-lead,
html[lang^="zh"] .sheet-notes-list li { line-height: 1.75; }

/* 3D experience embed */
.viewer-shell {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: #0d161c;
  aspect-ratio: 16 / 10;
}
@media (max-width: 700px) { .viewer-shell { aspect-ratio: 3 / 4; } }
.viewer-shell iframe { width: 100%; height: 100%; border: 0; display: block; }
.viewer-poster {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 1.5rem;
  text-align: center;
  border: 0;
  cursor: pointer;
  background: #0d161c;
  color: #fff;
}
.viewer-poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.5;
}
.viewer-poster > span {
  position: relative;
  display: grid;
  gap: 0.6rem;
  justify-items: center;
  max-width: 32ch;
}
.viewer-poster strong { font-size: var(--fs-xl); }
.viewer-poster small { color: #b9cbe8; font-size: var(--fs-xs); line-height: var(--lh-snug); }
.viewer-play {
  display: grid;
  place-items: center;
  width: 66px;
  height: 66px;
  border-radius: 50%;
  background: var(--red-600);
  box-shadow: 0 8px 30px rgb(224 16 24 / 45%);
}
.viewer-play svg { width: 26px; height: 26px; fill: #fff; }

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

.contact-list { margin: 0; display: grid; gap: 1.25rem; }
.contact-list > div {
  display: grid;
  gap: 0.15rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--line);
}
.contact-list > div:last-child { border-bottom: 0; padding-bottom: 0; }
.contact-list dt {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: var(--muted);
}
.contact-list dd { margin: 0; font-size: var(--fs-lg); font-weight: 600; }
.contact-list dd a { text-decoration: none; }
.contact-list dd a:hover { text-decoration: underline; }

/* --- forms ---------------------------------------------------------------- */

form label {
  display: block;
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink-2);
}
form input, form select, form textarea {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.75rem 0.9rem;
  min-height: 48px;
  font-weight: 400;
  color: var(--ink);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
form textarea { min-height: 130px; resize: vertical; }
form select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
    linear-gradient(135deg, var(--muted) 50%, transparent 50%);
  background-position: calc(100% - 19px) 50%, calc(100% - 14px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2.5rem;
}
form input:focus, form select:focus, form textarea:focus {
  border-color: var(--blue-600);
  box-shadow: 0 0 0 3px var(--blue-100);
  outline: none;
}
form input:user-invalid, form textarea:user-invalid { border-color: var(--red-600); }
form button[type="submit"] { width: 100%; }
@media (min-width: 560px) { form button[type="submit"] { width: auto; } }

/* --- footer --------------------------------------------------------------- */

.site-footer {
  padding-block: clamp(2.75rem, 6vw, 4.25rem) 0;
  background: var(--navy-900);
  color: #a9bcda;
  font-size: var(--fs-sm);
}
.footer-grid {
  display: grid;
  gap: 2rem clamp(1.5rem, 4vw, 3rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  padding-bottom: 2.5rem;
}

.site-footer h4 {
  margin-bottom: 0.9rem;
  color: #fff;
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
}
.site-footer ul { margin: 0; padding: 0; list-style: none; }
.site-footer li + li { margin-top: 0.55rem; }
.site-footer a { color: #a9bcda; text-decoration: none; }
.site-footer a:hover { color: #fff; text-decoration: underline; }

/* The switcher's no-interaction counterpart: four plain links, always rendered,
   whatever a browser does or does not do with <details>. */
.footer-langs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  padding-bottom: 1.4rem;
  font-size: var(--fs-xs);
}
.footer-langs a[aria-current="true"] { color: #fff; font-weight: 700; }

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  padding-block: 1.4rem;
  border-top: 1px solid rgb(255 255 255 / 12%);
  font-size: var(--fs-xs);
}
.footer-bottom p { margin: 0; }
.footer-bottom .spacer { margin-left: auto; }

/* --- misc ----------------------------------------------------------------- */

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.center { text-align: center; }
.mt-2 { margin-top: 2rem; }
.mt-3 { margin-top: 3rem; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- in-app browser hint --------------------------------------------------- */
.iab-hint {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: #06183c;
  color: #fff;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.25);
}
.iab-hint span { flex: 1; }
.iab-hint-close {
  flex: none;
  width: 30px;
  height: 30px;
  border: 1px solid rgba(255, 255, 255, 0.4);
  border-radius: 50%;
  background: none;
  color: #fff;
  font-size: 16px;
  cursor: pointer;
}

/* --- hero slogan, legal line and video cue --------------------------------- */
.hero-slogan {
  margin: 0 0 14px;
  font-size: clamp(0.95rem, 2.4vw, 1.2rem);
  font-weight: 700;
  letter-spacing: var(--track-caps);
  text-transform: uppercase;
  color: #f2c14b;
}
.hero-legal {
  margin: 22px 0 0;
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  color: rgb(255 255 255 / 55%);
}
.hero-watch { margin: 20px 0 0; }
.video-cue {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  color: #fff;
  text-decoration: none;
  font-size: var(--fs-sm);
  font-weight: 600;
}
.video-cue-play {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1.5px solid rgb(255 255 255 / 55%);
  border-radius: 50%;
  background: rgb(255 255 255 / 10%);
  flex: none;
}
.video-cue-play svg { width: 16px; height: 16px; fill: #fff; margin-left: 2px; }
.video-cue:hover .video-cue-play,
.video-cue:focus-visible .video-cue-play {
  background: var(--red, #c8102e);
  border-color: var(--red, #c8102e);
}
.video-cue span:last-child { border-bottom: 1px solid rgb(255 255 255 / 35%); }

/* --- video modal ----------------------------------------------------------- */
/* Opened with showModal(), so it lives in the top layer and no z-index on the
   page can get above it. The UA gives <dialog> a centred, auto-sized, bordered
   box — none of which we want, hence the reset.
   The dim is painted by the dialog ITSELF, which is sized to the viewport,
   rather than by ::backdrop. ::backdrop only exists for a top-layer element,
   so relying on it means the dimming quietly disappears on any path where
   showModal() did not take — and it is the one part of this whose rendering
   varies between browsers. A plain background on an element we control behaves
   the same everywhere. */
.video-modal {
  width: 100%;
  max-width: none;
  height: 100%;
  max-height: none;
  margin: 0;
  padding: 0;
  border: 0;
  background: rgb(4 12 28 / 88%);
  color: inherit;
  place-items: center;
}
.video-modal[open] { display: grid; }
/* Transparent, not dimmed: the dialog above already covers the viewport, so a
   second 88% layer behind it would stack to near-opaque. */
.video-modal::backdrop { background: transparent; }

/* Without showModal() the dialog is opened by its `open` attribute, which gets
   neither the top layer nor a ::backdrop — so it has to be positioned by hand.
   z-index 120 clears the sticky header at 100. */
.video-modal.is-fallback {
  position: fixed;
  inset: 0;
  z-index: 120;
}
.video-modal-box {
  position: relative;
  width: min(94vw, 440px);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
}
.video-modal-stage {
  background: #000;
  border-radius: 10px;
  overflow: hidden;
  line-height: 0;
}
.video-modal-stage video { width: 100%; max-height: 86vh; display: block; }
.video-modal-close {
  align-self: flex-end;
  margin-bottom: 10px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid rgb(255 255 255 / 35%);
  border-radius: 50%;
  background: rgb(255 255 255 / 8%);
  color: #fff;
  cursor: pointer;
}
.video-modal-close svg { width: 18px; height: 18px; }
.video-modal-close:hover { background: rgb(255 255 255 / 18%); }
body.modal-open { overflow: hidden; }

/* --- 3D page: model provenance footnote ------------------------------------ */
.model-note {
  margin: 22px 0 0;
  font-size: var(--fs-xs);
  color: var(--muted, #5c6b82);
}

/* --- phone: 3D model opens over the page ----------------------------------- */
.viewer-overlay {
  position: fixed;
  inset: 0;
  /* Above the sticky site header (z-index 100) — a half-covered model with the
     nav bar still on top is worse than no overlay at all. */
  z-index: 120;
  background: #0b1217;
}
.viewer-overlay iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}
.viewer-overlay-close {
  position: absolute;
  top: calc(10px + env(safe-area-inset-top));
  right: 10px;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: 1px solid rgb(255 255 255 / 30%);
  border-radius: 50%;
  background: rgb(11 18 23 / 82%);
  backdrop-filter: blur(10px);
  color: #fff;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

/* --- guides ------------------------------------------------------------- */
/* Long-form pages. The only place on the site with a real reading column, so
   the type is set for reading rather than for scanning: wider leading, a
   measure capped in ch, and headings that get room above rather than below. */

.guide-hero .guide-subtitle {
  font-size: clamp(1.05rem, 2.2vw, 1.3rem);
  color: #cddcf2;
  margin-bottom: 0;
}
.guide-hero-shot { margin: 0 0 2rem; }
.guide-hero-shot img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
}

/* Photographs inside the article. Same treatment as the hero shot, plus a
   caption — which is the part that earns the picture its place, so it is set as
   text to be read rather than as a label to be skipped. */
.guide-figure {
  margin: 2.25rem 0;
}
.guide-figure img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-lg);
}
.guide-figure figcaption,
.guide-hero-shot figcaption {
  margin-top: 0.7rem;
  padding-left: 0.9rem;
  border-left: 3px solid var(--red-600);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--ink-2);
}
/* The attribution a borrowed photograph has to carry. Quieter than the caption
   and on its own line, because it is a licence condition rather than something
   the reader is being asked to read. */
.guide-credit {
  display: block;
  margin-top: 0.3rem;
  font-size: var(--fs-xs);
  color: var(--muted);
}
.guide-credit a { color: inherit; text-underline-offset: 2px; }

/* Anchors for every section, so an assistant that quotes one paragraph can
   send the reader to that paragraph. The list is the only navigation a
   1,300-word page gets, and it is real markup rather than a script. */
.guide-contents {
  background: var(--bg-soft);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.25rem 1.5rem;
  margin-bottom: 2.5rem;
}
.guide-contents h2 {
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.6rem;
}
.guide-contents ol {
  margin: 0;
  padding-left: 1.1rem;
  display: grid;
  gap: 0.35rem;
  font-size: 0.95rem;
}

.guide-body { max-width: 68ch; }
.guide-body > p {
  line-height: 1.75;
  margin-bottom: 1.15rem;
}
.guide-body h2 {
  font-size: clamp(1.3rem, 2.6vw, 1.7rem);
  margin-top: 2.75rem;
  margin-bottom: 0.9rem;
  /* The heading is a link target; without this the sticky masthead lands on
     top of it when someone follows an anchor from a search result. */
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.guide-body h2:first-child { margin-top: 0; }

.guide-list {
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  display: grid;
  gap: 0.85rem;
}
.guide-list li {
  position: relative;
  padding-left: 1.5rem;
  line-height: 1.65;
}
.guide-list li::before {
  content: "";
  position: absolute;
  left: 0.15rem;
  top: 0.62em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--red-600);
}
.guide-list strong { color: var(--navy-900); }

.guide-seealso {
  list-style: none;
  padding: 0.9rem 1.1rem;
  margin: 0 0 1.75rem;
  border-left: 3px solid var(--blue-300);
  background: var(--blue-050);
  border-radius: 0 var(--radius) var(--radius) 0;
  display: grid;
  gap: 0.4rem;
  font-size: 0.95rem;
}


.guide-callout {
  margin-top: 3rem;
  padding: 1.6rem 1.75rem;
  background: var(--navy-900);
  color: #dbe6f6;
  border-radius: var(--radius-lg);
}
.guide-callout h2 {
  color: #fff;
  margin: 0 0 0.6rem;
  font-size: 1.25rem;
  scroll-margin-top: calc(var(--header-h) + 1rem);
}
.guide-callout p { margin: 0; line-height: 1.7; }

/* Index */
.guide-cards { display: grid; gap: 2rem; }
.guide-card {
  display: grid;
  gap: 1.25rem;
  align-items: start;
}
@media (min-width: 760px) {
  .guide-card { grid-template-columns: 300px 1fr; gap: 1.75rem; }
}
.guide-card-img img {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  display: block;
}
.guide-card h3 { margin: 0 0 0.25rem; font-size: 1.25rem; }
.guide-card h3 a { color: inherit; text-decoration: none; }
.guide-card h3 a:hover { color: var(--red-600); }
.guide-card-sub { color: var(--muted); margin: 0 0 0.7rem; }

/* --- quote form status ---------------------------------------------------- */

/* Off-screen rather than display:none — a bot that skips hidden fields is
   exactly the bot worth catching, and a real person never reaches it because
   the wrapper is aria-hidden and the input is tabindex=-1. */
.form-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form-status:empty { display: none; }
.form-status {
  margin: 0;
  padding: 0.85rem 1rem;
  border-radius: var(--radius);
  font-size: 0.95rem;
  line-height: 1.6;
  border-left: 3px solid var(--line);
  background: var(--bg-soft);
}
.form-status strong { display: block; }
.form-status a { font-weight: 600; }
.form-status[data-state="ok"] {
  border-left-color: #1c8b4b;
  background: #eefaf2;
}
/* Failure is louder than success on purpose: it is the state that needs the
   reader to do something, and what it asks them to do is right there in it. */
.form-status[data-state="fail"] {
  border-left-color: var(--red-600);
  background: #fdeef0;
}
