/* ==========================================================================
   Pixelstead — Marketing site for the studio itself.
   Round 3: high-end editorial visual system. Precise, calm, confident,
   contemporary, editorial, technically capable. Deep ink + warm paper +
   soft stone secondary + cobalt blue used only for key actions, numbering
   and small brand accents. No gradients, no glassmorphism, no floating
   icon decoration, no stock-photo people, no large orange surfaces.
   8px spacing rhythm throughout. Single max content width sitewide.
   ========================================================================== */

:root {
  /* ---- Palette ------------------------------------------------------- */
  --color-ink: #14171c;          /* near-black deep ink — primary dark */
  --color-ink-soft: #3a4048;
  --color-navy: #12171e;         /* hero / project-scope dark block */
  --color-paper: #faf8f4;        /* warm off-white — primary light bg */
  --color-stone: #efebe2;        /* soft stone grey — secondary bg */
  --color-stone-line: #ddd6c8;   /* hairline on stone */
  --color-line: #ddd7ca;         /* hairline on paper */
  --color-line-dark: rgba(255, 255, 255, 0.14);
  --color-white: #ffffff;
  --color-muted: #686f78;        /* body-muted on light backgrounds */
  --color-muted-dark: #a8afba;   /* body-muted on dark backgrounds */
  --color-accent: #0047fc;       /* cobalt blue — key actions/numbering only */
  --color-accent-dark: #0038cc;
  --color-accent-on-dark: #6f93ff; /* lighter tint for legibility on navy/ink */

  /* ---- Type ------------------------------------------------------------ */
  --font-display: Georgia, "Times New Roman", "Noto Serif", serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* ---- Layout ----------------------------------------------------------- */
  --max-width: 1200px;
  --radius: 10px;
  --radius-lg: 14px;

  /* ---- 8px spacing rhythm ------------------------------------------------ */
  --sp-1: 8px;
  --sp-2: 16px;
  --sp-3: 24px;
  --sp-4: 32px;
  --sp-5: 48px;
  --sp-6: 64px;
  --sp-7: 96px;
  --sp-8: 128px;
}

/* ---------------------------------------------------------------------- */
/* Reset                                                                  */
/* ---------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: auto;
  overflow-x: hidden;
  width: 100%;
}
body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-ink);
  background: var(--color-paper);
  line-height: 1.6;
  font-size: 17px;
  width: 100%;
  /* No overflow-x here (kept on html only, above). CSS couples overflow-x
     and overflow-y: an element with overflow-x set to anything but
     'visible' gets its overflow-y computed as 'auto', not 'visible'. With
     overflow-x:hidden on BOTH html and body, body silently became its own
     independent scroll container (distinct from the viewport) — and
     position:sticky elements inside it (the header) stuck to that inert
     body box instead of the viewport, so the "sticky" header was never
     actually pinned on screen at any scroll position. Horizontal-overflow
     clipping stays fully effective via html's overflow-x:hidden alone. */
}
img { max-width: 100%; display: block; }
a { color: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4, p { margin: 0; }
button { font: inherit; }

@media (min-width: 1024px) {
  body { font-size: 18px; }
}

/* Deliberate, branded focus style — never removed, never hidden. Used
   sitewide so a keyboard/QA focus ring reads as an intentional design
   element (cobalt blue) rather than a stray browser default. */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: 2px;
}
.hero :focus-visible,
.final-cta :focus-visible,
.site-footer :focus-visible {
  outline-color: var(--color-accent-on-dark);
}

/* [inert] ships as a UA style in most browsers already; made explicit in
   case a browser lacks it — background content must be visually and
   interactively unreachable while the mobile menu is open. */
[inert] {
  pointer-events: none;
  cursor: default;
}

/* Scroll Reveal (v1.2) — a restrained, one-shot fade-up on specific
   homepage content modules (js/main.js's initScrollReveal marks each with
   .is-revealed the first time it enters the viewport, via
   IntersectionObserver, then stops observing it — it can never re-hide or
   re-play). Hero, pricing, Footer, Navigation and legal-page body text are
   never given [data-reveal] at all, so this block has no effect on them.
   The whole thing is scoped under html.js-reveal (added by a synchronous
   inline <script> in <head>, before first paint) so that with JS disabled
   — where that class is never added — [data-reveal] elements simply keep
   the browser's normal default (opacity: 1, no transform) and are visible
   immediately; nothing here can leave content permanently hidden or cause
   an initial flash, because the "hidden until revealed" state only exists
   at all once JS has already committed to revealing things. Only opacity
   and transform are animated (both paint-only, not layout — this cannot
   contribute to CLS), and both are unconditionally cleared under
   prefers-reduced-motion, where the feature is fully off, not just faster. */
html.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.48s cubic-bezier(.22, 1, .36, 1), transform 0.48s cubic-bezier(.22, 1, .36, 1);
}
html.js-reveal [data-reveal].is-revealed {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  html.js-reveal [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

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

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}

.eyebrow {
  display: inline-block;
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-accent-dark);
  font-weight: 600;
  margin-bottom: var(--sp-2);
}

.section-heading {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.75rem, 1.35rem + 1.7vw, 2.75rem);
  line-height: 1.18;
  color: var(--color-ink);
  /* Let headings use the room their section already provides. The old
     20ch ceiling created artificial two-line headings on wide screens —
     especially in Chinese, where ch does not represent a CJK character.
     On narrow screens the container remains the natural wrapping limit. */
  max-width: 100%;
  text-wrap: balance;
  word-break: normal;
  overflow-wrap: normal;
  hyphens: none;
}

:lang(zh) .section-heading {
  line-break: strict;
}

.section-lede {
  margin-top: var(--sp-2);
  color: var(--color-muted);
  font-size: 1.0625rem;
  line-height: 1.65;
  max-width: 58ch;
}

.section-head {
  margin-bottom: var(--sp-6);
}

/* Skip link */
.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--color-ink);
  color: var(--color-white);
  padding: 10px 16px;
  z-index: 200;
}
.skip-link:focus {
  left: 12px;
  top: 12px;
}

/* ---------------------------------------------------------------------- */
/* Buttons                                                                */
/* ---------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 15px 30px;
  border-radius: var(--radius);
  font-weight: 600;
  font-size: 0.98rem;
  letter-spacing: 0.005em;
  text-decoration: none;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease, opacity 0.18s ease;
}
.btn-primary {
  background: var(--color-accent);
  color: var(--color-white);
  border-color: var(--color-accent);
}
.btn-primary:hover { background: var(--color-accent-dark); border-color: var(--color-accent-dark); }
.btn-secondary {
  background: transparent;
  color: var(--color-ink);
  border-color: var(--color-ink);
}
.btn-secondary:hover { background: var(--color-ink); color: var(--color-white); }
.hero .btn-secondary,
.final-cta .btn-secondary {
  border-color: rgba(255, 255, 255, 0.5);
  color: var(--color-white);
}
.hero .btn-secondary:hover,
.final-cta .btn-secondary:hover { background: rgba(255, 255, 255, 0.1); }
.btn-block { width: 100%; }
.btn-small { padding: 10px 20px; font-size: 0.9rem; }

/* Visual cue that a WhatsApp CTA is not yet configured — deliberately
   subtle (no dashed box) so a pre-launch state doesn't read as a broken
   button. aria-disabled + title (set in main.js) carry the same
   information for assistive tech; :focus-visible above still applies
   normally to any element that keeps it in the tab order. */
.is-placeholder-link {
  cursor: not-allowed;
  opacity: 0.82;
}

/* ---------------------------------------------------------------------- */
/* Header + nav                                                          */
/* ---------------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(250, 248, 244, 0.94);
  border-bottom: 1px solid var(--color-line);
  backdrop-filter: saturate(140%) blur(6px);
  /* Scroll show/hide (Visual & Interaction round) — js/main.js toggles
     .is-nav-hidden on this element based on scroll direction; a plain
     transform (not display/height/margin) is used specifically so hiding
     and re-showing the header never changes layout height and can never
     cause a reflow/CLS. transform is also what lets .site-header stay
     `position: sticky` throughout — sticky elements respond to their own
     transform normally. */
  transform: translateY(0);
  transition: transform 0.25s ease;
}
.site-header.is-nav-hidden {
  transform: translateY(-100%);
}
@media (prefers-reduced-motion: reduce) {
  .site-header { transition: transform 0.001ms linear; }
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 76px;
}
.brand {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

/* Logo sizing: fixed height, width auto — never stretched, no shadow/glow.
   Source PNGs are losslessly trimmed to their true glyph bounds (see
   README), so a modest CSS height now reads clearly rather than sitting
   inside a mostly-transparent canvas. */
.brand-logo {
  height: 34px;
  width: auto;
  display: block;
}

/* Desktop-only inline nav — separate from the mobile slide-in panel below,
   so it never needs the fixed-position/containing-block juggling a single
   shared element would require. Hidden on mobile. */
.desktop-nav,
.desktop-cta,
.header-lang-switch {
  display: none;
}

.nav-toggle {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 5px;
  width: 44px;
  height: 44px;
  background: transparent;
  border: 1px solid var(--color-line);
  border-radius: var(--radius);
  cursor: pointer;
}
.nav-toggle .bar {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--color-ink);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.nav-toggle[aria-expanded="true"] .bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .bar:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.nav-backdrop {
  /* Starts below the sticky header (76px) rather than inset:0, so it never
     overlaps the header's own stacking context — the header stays above
     both the backdrop and the panel, and stays clickable while the menu
     is open. */
  position: fixed;
  top: 76px;
  right: 0;
  bottom: 0;
  left: 0;
  background: rgba(16, 21, 31, 0.4);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 90;
}
.nav-backdrop.is-open {
  opacity: 1;
  pointer-events: auto;
}

/* ---------------------------------------------------------------------- */
/* Language switcher — EN | 中文 | BM. Plain text links, no flags, no       */
/* dropdown. Restrained: current language reads clearly via a thin accent  */
/* underline, not a filled pill or bright color block.                    */
/* ---------------------------------------------------------------------- */
.lang-switch {
  /* display is intentionally NOT set here: .header-lang-switch controls
     visibility in the header (none on mobile, inline-flex from 768px —
     see the .desktop-nav/.desktop-cta/.header-lang-switch rules above),
     and .primary-nav .lang-switch below sets it for the mobile-menu copy.
     A bare `display: flex` here would win the cascade (equal specificity,
     later in file) and defeat the mobile hide — exactly the Round 3.1
     class of bug, avoided here rather than repeated. */
  align-items: center;
  gap: 10px;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}
.lang-switch a {
  text-decoration: none;
  color: var(--color-muted);
  padding: 4px 1px;
  border-bottom: 2px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.lang-switch a:hover { color: var(--color-ink); }
.lang-switch a[aria-current="page"] {
  color: var(--color-ink);
  border-bottom-color: var(--color-accent);
}
.lang-switch .lang-sep {
  color: var(--color-line);
  font-size: 0.8rem;
}
.hero .lang-switch a[aria-current="page"],
.final-cta .lang-switch a[aria-current="page"] {
  border-bottom-color: var(--color-accent-on-dark);
}

/* Mobile menu's language row — sits inside .primary-nav, so every rule
   here is written at .primary-nav .lang-switch a specificity (0,0,3,1) on
   purpose: .primary-nav a alone (0,0,1,1) must never win here again (see
   the Round 3.1 mobile-CTA fix — same class of bug, avoided proactively). */
.primary-nav .lang-switch {
  display: flex;
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
  gap: 14px;
  font-size: 0.92rem;
}
.primary-nav .lang-switch a {
  display: inline;
  padding: 4px 1px;
  border-bottom: 2px solid transparent;
  color: var(--color-muted);
  font-weight: 600;
}
.primary-nav .lang-switch a[aria-current="page"] {
  color: var(--color-ink);
  border-bottom-color: var(--color-accent);
}

.primary-nav {
  position: fixed;
  top: 0;
  right: 0;
  height: 100%;
  width: min(84vw, 340px);
  background: var(--color-white);
  border-left: 1px solid var(--color-line);
  transform: translateX(100%);
  transition: transform 0.28s ease;
  z-index: 95;
  padding: 96px 32px 32px;
  overflow-y: auto;
}
.primary-nav.is-open { transform: translateX(0); }

.primary-nav ul {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.primary-nav a {
  display: block;
  padding: 14px 2px;
  text-decoration: none;
  font-weight: 600;
  font-size: 1.02rem;
  color: var(--color-ink);
  border-bottom: 1px solid var(--color-line);
}
.primary-nav .nav-cta { margin-top: var(--sp-3); }

/* .primary-nav a (above) sets color: var(--color-ink) with specificity
   (0,1,1), which beats .btn-primary's (0,1,0) and was overriding the
   mobile "Start a Project" CTA to black-on-blue. Re-assert white text at
   the same (0,2,1) specificity the CTA actually needs it at, for every
   interactive state — no !important required. */
.primary-nav a.btn-primary,
.primary-nav a.btn-primary:hover,
.primary-nav a.btn-primary:active {
  color: var(--color-white);
  border-bottom: none;
}
.primary-nav a.btn-primary.is-placeholder-link {
  color: var(--color-white);
}

/* Mobile menu scroll lock. `overflow: hidden` alone does not reliably stop
   background scrolling on real iOS Safari (rubber-band/elastic scroll still
   moves the document under a `position: sticky` header). Pin the body at
   its current scroll offset instead: js/main.js sets `top` to the negative
   of the saved scrollY right before this class is added, and clears it
   (restoring scroll position with `scrollTo(..., { behavior: "instant" })`)
   right after the class is removed. */
body.nav-scroll-lock {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

/* While locked, body's own overflow:hidden makes it a scroll container in
   its own right, which would hijack .site-header's `position: sticky`
   (sticky sticks to the nearest scroll-container ancestor, not necessarily
   the viewport) — the header would then sit at body's local top, which is
   off-screen once body is pinned via `top: -scrollY`. Force the header to
   `position: fixed` for the duration of the lock instead: fixed always
   resolves against the viewport, independent of any ancestor's overflow. */
body.nav-scroll-lock .site-header {
  position: fixed;
  left: 0;
  right: 0;
}

@media (min-width: 768px) {
  .nav-toggle { display: none; }
  .nav-backdrop { display: none; }
  /* The mobile slide-in panel is not used at all at this breakpoint —
     the .desktop-nav / .desktop-cta pair below takes over instead. */
  .primary-nav { display: none; }

  .desktop-nav,
  .desktop-cta,
  .header-lang-switch {
    display: inline-flex;
  }
  .desktop-nav ul {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 32px;
  }
  .desktop-nav a {
    position: relative;
    text-decoration: none;
    font-weight: 600;
    font-size: 0.94rem;
    color: var(--color-ink-soft);
    padding: 6px 0;
    white-space: nowrap;
  }
  .desktop-nav a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 100%;
    bottom: 0;
    height: 1px;
    background: var(--color-accent);
    transition: right 0.2s ease;
  }
  .desktop-nav a:hover { color: var(--color-ink); }
  .desktop-nav a:hover::after { right: 0; }
  .site-header .container {
    gap: 24px;
    justify-content: flex-start;
  }
  .brand { margin-right: auto; }
  .desktop-nav ul { gap: 22px; }
  .desktop-cta.btn { padding: 12px 22px; font-size: 0.9rem; }
}
@media (min-width: 1100px) {
  .site-header .container { gap: 32px; }
  .desktop-nav ul { gap: 32px; }
  .desktop-cta.btn { padding: 12px 24px; font-size: 0.92rem; }
}

/* ---------------------------------------------------------------------- */
/* Hero                                                                   */
/* ---------------------------------------------------------------------- */
.hero {
  padding: 72px 0 var(--sp-7);
  background: var(--color-navy);
  color: var(--color-white);
}
.hero-grid {
  display: grid;
  gap: var(--sp-6);
}
.hero-eyebrow {
  color: var(--color-accent-on-dark);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: var(--sp-3);
}
/* Hero heading — Visual & Interaction round: re-tuned so the headline
   balances onto sensible line counts at every width instead of leaving an
   orphaned word/character on its own last line. text-wrap: balance (with
   a no-op fallback in unsupported browsers, since it's purely additive)
   does the actual line-break distribution; font-size/max-width here just
   set the type scale and the box it has to balance within. max-width is
   tuned per language via :lang() — English/Malay/Chinese have very
   different character widths and copy lengths, so one shared ch-based
   width does not give any of them a good balance (see the 992px+ rule
   below for the full per-language breakdown). Copy itself is unchanged. */
.hero h1 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.5rem, 2rem + 3vw, 2.875rem);
  line-height: 1.12;
  max-width: 13ch;
  text-wrap: balance;
}
:lang(zh) .hero h1 { max-width: 11em; letter-spacing: 0.01em; }
:lang(ms) .hero h1 { max-width: 16ch; }
.hero-sub {
  margin-top: var(--sp-3);
  font-size: 1.1rem;
  line-height: 1.65;
  color: #c9cfdb;
  max-width: 43ch;
}
.hero-actions {
  margin-top: var(--sp-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
.btn-quiet {
  color: #dce3ef;
  background: transparent;
  border-color: rgba(255, 255, 255, 0.22);
}
.btn-quiet:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.52);
  background: rgba(255, 255, 255, 0.05);
}
.btn-quiet span { margin-left: 6px; }
/* Low-key price cue — smaller than .hero-sub, well below the H1, muted
   tint rather than white or accent, so it reads as a quiet footnote to the
   headline rather than competing with it. */
.hero-price-hint {
  margin-top: var(--sp-3);
  font-size: 0.92rem;
  color: var(--color-muted-dark);
}

/* Hero showcase — Slideshow visual-fix round 3: the three Demo images are
   now freshly-captured, real screenshots of the three live Demo sites
   themselves (kilau/lumea/veraan.pixelstead.com), taken at an identical
   1440×900 viewport and saved at that exact 1440×900 pixel size (see
   README.md for the full capture method/provenance). Because every slide
   is now the SAME pixel dimensions as the `.hero-slides` box below (16:10,
   i.e. 1440:900), there is no more letterbox/pillarbox band to fill —
   `object-fit: cover` at matching ratios is a lossless 1:1 fit, not an
   actual crop, so no per-slide background colour is needed or used any
   more (the previous round's sampled-edge-colour fills are gone; nothing
   is "disguising" a ratio mismatch, because there no longer is one). The
   fixed aspect-ratio frame is kept so switching slides never changes page
   height (no CLS).

   Autoplay (retimed in v1.2 from 6s to 3.5s/slide): the Slideshow
   advances every 3.5s, KILAU → LUMÉA → VERAAN → KILAU, via a single
   setInterval managed in js/main.js (initHeroSlideshow's restartAutoplay
   always clears any existing timer before creating a new one, so it can
   never double up or accelerate). It pauses on hover, on keyboard focus
   anywhere inside the region, and whenever the tab is hidden — and
   resumes with a full fresh 3.5s window, never a shortened "wherever it
   left off" tick. It never starts at all under prefers-reduced-motion:
   reduce, leaving arrows/keyboard/swipe as the only way to change
   slides. There is still no Play/Pause control and no dot indicators —
   removed entirely in round 1, not reinstated.

   Slide switching is a restrained cross-fade (opacity only, ~450ms) —
   deliberately not the large horizontal slide/track animation of a
   typical e-commerce banner carousel. This requires every slide to stay
   present in the box (stacked via position:absolute) rather than using
   the `hidden` attribute to remove the other two outright, so the a11y/
   tab-order exclusion that `hidden` used to give for free is now done
   explicitly: the inactive slides carry `aria-hidden="true"` and
   `tabindex="-1"` (kept in sync with the `.is-active` class by
   js/main.js), which removes them from the accessibility tree and the
   tab order exactly as `hidden` did, while still allowing the opacity
   transition to render. Progressive enhancement is unchanged — slide 0
   ships with the `.is-active` class and no aria-hidden/tabindex in the
   static HTML, so with JS disabled the Hero still shows one complete,
   clickable KILAU slide (the other two sit at opacity:0,
   pointer-events:none, aria-hidden, out of the tab order — inert, not
   just invisible). */
.hero-showcase {
  display: block;
}
.hero-slideshow {
  position: relative;
}
.hero-slides {
  position: relative;
  margin: 0;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-navy);
  aspect-ratio: 16 / 10;
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.28);
}
.hero-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
}
.hero-slide.is-active {
  opacity: 1;
  pointer-events: auto;
}
.hero-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}
@media (prefers-reduced-motion: reduce) {
  .hero-slide { transition: none; }
}

/* Arrows — v1.2 redesign: low-profile edge-hugging capsules (half-ellipse,
   flush against the image's own left/right edge) instead of the previous
   round's free-floating circular button. The capsule's own visible
   background stays at low opacity at all times (including touch — no
   `opacity:0`-until-hover any more, so there's nothing to fail to trigger
   on a device with no hover), only strengthening a little on desktop
   hover/focus; the arrow glyph itself is a separate element kept at full
   opacity throughout, so it always reads clearly even while the capsule
   behind it stays faint. Never full opacity, never a heavy shadow, never
   a thick border, so it can't compete with the Demo screenshot itself.
   The visible capsule is intentionally narrow (32×64px); a ::before
   hit-slop (out of flow, invisible) brings the real clickable area to
   at least 44×64px without widening what's actually drawn. */
.hero-slide-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 64px;
  border: none;
  background: rgba(8, 10, 15, 0.14);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 1;
  transition: background-color 0.2s ease;
}
.hero-slide-arrow::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 64px;
  transform: translate(-50%, -50%);
}
.hero-slide-arrow span {
  color: rgba(255, 255, 255, 0.82);
  font-size: 1.2rem;
  line-height: 1;
  transition: color 0.2s ease;
}
.hero-slide-prev {
  left: 0;
  border-radius: 0 999px 999px 0;
}
.hero-slide-next {
  right: 0;
  border-radius: 999px 0 0 999px;
}
.hero-slideshow:hover .hero-slide-arrow,
.hero-slide-arrow:focus-visible {
  background: rgba(8, 10, 15, 0.34);
}
.hero-slideshow:hover .hero-slide-arrow span,
.hero-slide-arrow:focus-visible span {
  color: rgba(255, 255, 255, 1);
}
.hero-slide-arrow:hover { background: rgba(8, 10, 15, 0.42); }

@media (min-width: 992px) {
  .hero { padding: 96px 0 112px; }
  .hero-grid {
    grid-template-columns: 0.78fr 1.22fr;
    align-items: center;
    gap: 56px;
    /* Visual-fix round: the whole Hero row (both columns, same 0.92:1.08
       split) is ~18-19% wider than the sitewide 1200px container, so the
       Slideshow has meaningfully more room to show full, uncropped concept
       screenshots — without narrowing the left (heading) column, which
       actually grows too. Still short of the raw 1440px+ viewport, so
       content never sits flush against the screen edge. */
    max-width: 1480px;
  }
  /* Desktop target (per visual QA): ~64–72px at 1440px viewport width —
     was clamp(4rem,1.2rem+4.5vw,5.5rem), which resolved to 84px at 1440
     (well past the 5.5rem/88px ceiling's headroom) and, combined with the
     old 14ch max-width, could leave a lone trailing word/character on its
     own line, especially for the Chinese headline (a ch-based width is
     measured against the Latin "0" glyph, so it does not map predictably
     onto CJK character counts). 1rem + 3.6vw resolves to ~67.8px at
     1440px (within the 64–72px range) and to the 4rem/64px floor at and
     below ~1333px viewport width, so it doesn't jump oddly right at the
     992px breakpoint. */
  .hero h1 { font-size: clamp(3.75rem, 1rem + 3.45vw, 4.4rem); line-height: 1.04; max-width: 13ch; }
  /* Chinese: widening the Hero row above (max-width: 1400px) grew the
     text column from ~486px to ~578px at 1440px viewport — enough that
     the 16-character headline now balances onto two lines (an even 8/8
     split, verified via Playwright up to 72px) at the SAME 64-72px scale
     as EN/MS, so Chinese no longer needs a smaller font-size than the
     shared one (a prior round had to shrink it to ~59px to fit the
     narrower pre-widening column). Only the floor is lowered (3.5rem vs
     the shared 4rem) so 992-1200px — where the column is narrower than
     1440px and 64px would force a 3rd line with an awkward split — still
     gets a size that balances cleanly; the ceiling (4.5rem/72px) and the
     3.6vw slope both match the shared rule exactly, landing on the
     identical 67.84px at 1440px. */
  :lang(zh) .hero h1 { font-size: clamp(3.35rem, 1rem + 3.25vw, 4.2rem); max-width: 10.5em; letter-spacing: 0; }
  :lang(ms) .hero h1 { font-size: clamp(3.45rem, 1rem + 3.1vw, 4rem); max-width: 15ch; }
  .hero-sub { font-size: 1.15rem; }
}
/* Mobile/tablet (<992px): the Slideshow (see above) already shows exactly
   one image at a time, at every width — no separate rule is needed here
   any more (this used to hide the two detail-crop thumbnails, which no
   longer exist in the markup). */

/* ---------------------------------------------------------------------- */
/* Section rhythm — deliberately uneven, not a single repeated block      */
/* ---------------------------------------------------------------------- */
.section { padding: var(--sp-7) 0; }
.section-alt { background: var(--color-stone); }
/* Price Adjustment round — extracted from a repeated inline style="" that
   had crept into the first <section> of each of the 6 Packages/FAQ
   sub-pages (packages-top, faq-top): those pages have no hero above them,
   so their first section needs a shorter top gap than the site-wide
   --sp-7 default. padding-bottom is left at the .section default
   (--sp-7 = 96px) in all cases — it was never actually being overridden,
   the old inline style just restated the existing default. */
.section-top-tight { padding-top: var(--sp-5); }
#who-for { padding: var(--sp-6) 0 var(--sp-7); }
/* Services — Visual & Interaction round: shortened from --sp-7/--sp-8
   (96px/128px) to --sp-6/--sp-7 (64px/96px), and its own section-head
   gap tightened from the sitewide --sp-6 (96px) default to --sp-4
   (32px) — together with the 3-column layout above, this is what
   visibly shortens the whole Services block. */
#services { padding: var(--sp-6) 0; }
#services .section-head { margin-bottom: var(--sp-4); }
#process { padding: var(--sp-6) 0 var(--sp-7); }
#responsibilities { padding: var(--sp-6) 0; }
#faq { padding: var(--sp-7) 0 var(--sp-8); }
@media (min-width: 992px) {
  #services { padding: var(--sp-7) 0; }
}

/* Round 4.2 — mobile/tablet visual rhythm. Desktop (>=900px) keeps every
   value above untouched (still spacious/premium); below 900px, section
   padding and the intro-to-content gap are tightened. This is on top of —
   not instead of — the content/IA reductions above (fewer Services,
   fewer Who-For items, fewer Process steps, 6-item FAQ, collapsed RM500
   exclusions): padding alone was deliberately not used as the primary
   lever for the ~25-35% mobile height reduction target. */
@media (max-width: 899px) {
  .section { padding: var(--sp-5) 0; }
  #who-for { padding: var(--sp-4) 0 var(--sp-5); }
  #services { padding: var(--sp-5) 0; }
  #process { padding: var(--sp-4) 0 var(--sp-5); }
  #responsibilities { padding: var(--sp-4) 0; }
  #faq { padding: var(--sp-5) 0 var(--sp-6); }
  .section-head { margin-bottom: var(--sp-4); }
  .who-detail { gap: var(--sp-4); }
  .packages-grid { gap: var(--sp-5); }
  .renewal-grid { gap: var(--sp-3); }
  /* Round 4.2.1 — Final CTA tightened further on mobile, matching the
     rhythm of the sections above it rather than keeping its own larger
     desktop padding. */
  .final-cta { padding: var(--sp-5) 0; }
}

/* ---------------------------------------------------------------------- */
/* Who Pixelstead Is For — an editorial statement, not two matching cards */
/* ---------------------------------------------------------------------- */
.who-grid {
  display: grid;
  gap: var(--sp-5);
}
.who-statement { max-width: 42ch; }
.who-lede {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 1.25rem + 1vw, 2rem);
  line-height: 1.35;
  color: var(--color-ink);
}
.who-lede--fixed-lines span {
  display: block;
  max-width: 100%;
}
.who-detail {
  display: grid;
  gap: var(--sp-5);
}
.who-block h3 {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.98rem;
  letter-spacing: 0.02em;
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--color-line);
  margin-bottom: var(--sp-1);
}
.who-index {
  font-family: var(--font-display);
  color: var(--color-accent);
  font-size: 1rem;
}
.who-list li {
  padding: 12px 0;
  border-bottom: 1px solid var(--color-line);
  font-size: 0.98rem;
  color: var(--color-ink-soft);
}
.who-list li:last-child { border-bottom: none; }

@media (min-width: 900px) {
  .who-grid { grid-template-columns: 0.85fr 1.3fr; gap: var(--sp-7); }
  .who-detail { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }
}

/* ---------------------------------------------------------------------- */
/* Services — editorial numbered list, deliberately not a grid of cards   */
/* ---------------------------------------------------------------------- */
/* Services — Visual & Interaction round: a true 3-column row at >=900px
   (per spec — 768-899px keeps the existing compact single-column list
   below rather than forcing three columns into a width that would
   squeeze the copy; 320/375px never gets more than one column either).
   Row padding is tightened throughout (mobile --sp-4 -> --sp-3, tablet
   --sp-5 -> --sp-4) and the section-head-to-list gap is tightened
   specifically for Services (see the #services rule further below),
   which together with the 3-column layout is what shortens the section's
   overall height. */
.services-list {
  border-top: 1px solid var(--color-line);
}
.service-row {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sp-3);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--color-line);
  transition: background-color 0.2s ease;
}
.service-index {
  font-family: var(--font-display);
  font-size: 1.6rem;
  color: var(--color-accent);
  line-height: 1.3;
}
.service-body h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.3rem;
  margin-bottom: 6px;
}
.service-body p {
  color: var(--color-muted);
  font-size: 1rem;
  line-height: 1.6;
  max-width: 58ch;
}
@media (min-width: 768px) {
  .service-row {
    grid-template-columns: 110px 1fr;
    align-items: baseline;
    padding: var(--sp-4) 0;
  }
  .service-row:hover { background: rgba(0, 71, 252, 0.025); }
  .service-index { font-size: 1.9rem; }
}
@media (min-width: 768px) and (max-width: 899px) {
  /* A light stagger on alternating rows keeps the single-column list from
     reading as a flat, generic table while staying purely typographic —
     only relevant while Services is still a single-column list; the
     3-column layout at >=900px (below) replaces this entirely. */
  .service-row:nth-child(even) .service-body { margin-left: var(--sp-4); }
}
@media (min-width: 900px) {
  .services-list {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    column-gap: var(--sp-5);
    align-items: start;
    border-top: none;
  }
  .service-row {
    display: block;
    padding: 0 var(--sp-4);
    border-bottom: none;
    border-left: 1px solid var(--color-line);
  }
  .service-row:first-child { border-left: none; padding-left: 0; }
  .service-row:last-child { padding-right: 0; }
  .service-index { display: block; font-size: 1.8rem; margin-bottom: var(--sp-2); }
}

/* ---------------------------------------------------------------------- */
/* Selected Concept Work — large case-study features, not product cards  */
/* ---------------------------------------------------------------------- */
/* Selected Work heading — the old blue eyebrow label above the heading is
   removed from the HTML, so #work's own .section-head is slightly shorter
   than the sitewide default. Heading width now follows the shared responsive
   rule above: use available room first and wrap only when the viewport
   genuinely requires it. */
#work .section-head { margin-bottom: var(--sp-5); }

.case-list {
  margin-top: var(--sp-2);
}
.case-study {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-6) 0;
  border-top: 1px solid var(--color-line);
}
.case-study:last-child { padding-bottom: 0; }
.case-image {
  display: block;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.case-image img {
  width: 100%;
  height: auto;
  display: block;
  transition: transform 0.5s ease;
}
.case-image:hover img { transform: scale(1.012); }
.case-copy {
  display: flex;
  flex-direction: column;
  align-self: center;
}
.case-category {
  font-size: 0.78rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-muted);
  font-weight: 600;
}
.case-copy h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.5rem, 1.3rem + 0.7vw, 1.9rem);
  margin-top: var(--sp-1);
}
.case-goal {
  color: var(--color-muted);
  font-size: 1rem;
  line-height: 1.6;
  margin-top: var(--sp-2);
  max-width: 46ch;
}
.case-copy .btn {
  margin-top: var(--sp-3);
  align-self: flex-start;
}
/* Concept declaration: kept in full, but presented as quiet meta text —
   never a bordered/coloured warning box, never more prominent than the
   project title or CTA. */
.case-meta {
  margin-top: var(--sp-3);
  font-size: 0.82rem;
  color: var(--color-muted);
}
/* .work-footnote ("Each preview opens... in a new tab") removed this
   round (Visual & Interaction round, §8) — the sentence was deleted as a
   real HTML element on all three language pages (verified 0 remaining
   occurrences by source search, not just visually hidden), and this now
   -unused rule is removed with it. .case-study:last-child already zeroes
   its own bottom padding, so Selected Work's bottom spacing tightens
   automatically now that no trailing paragraph follows the last card. */

@media (min-width: 900px) {
  .case-study {
    grid-template-columns: 1.15fr 0.85fr;
    gap: var(--sp-6);
    padding: var(--sp-7) 0;
    align-items: center;
  }
  .case-study--image-right .case-image { order: 2; }
  .case-study--image-right .case-copy { order: 1; padding-right: var(--sp-4); }
  .case-study--image-left .case-copy { padding-left: var(--sp-4); }
}

/* Round 3.1 — mobile-only refinements, phone widths only (<=600px).
   Corrected from an earlier max-width: 899px draft, which incorrectly
   also matched 768px — .case-study's own layout breakpoint is 900px, but
   these two rules are scoped to phones specifically, not "everything
   below the desktop Selected Work breakpoint." Selected Work at 768px
   and above (768px included) is unchanged from Round 3. */
@media (max-width: 600px) {
  /* Tighten the intro -> first project gap. Only the section intro's
     bottom margin and the first case study's top padding change; every
     other .case-study keeps its full padding, so spacing *between*
     projects is unaffected. */
  #work .section-head {
    margin-bottom: var(--sp-4);
  }
  .case-study:first-child {
    padding-top: var(--sp-3);
  }

  /* The three case-study screenshots read "too flat" at mobile widths,
     with on-page content hard to make out. object-fit: cover crops each
     image's width (not its height) toward a squarer 16:10 frame, which
     visually zooms in on the subject; object-position is tuned per
     image so its heading block and key visual both stay in frame. */
  .case-image img {
    aspect-ratio: 16 / 10;
    object-fit: cover;
  }
  .case-image--lumea img { object-position: 27% 50%; }
  .case-image--kilau img { object-position: center; }
  .case-image--veraan img { object-position: 21% 50%; }
}

/* ---------------------------------------------------------------------- */
/* Packages — Round 4. A tiered, honestly-scoped structure: thin hairlines,*/
/* numbering and whitespace carry the hierarchy, not boxed cards, shadows */
/* or a loud "Most Popular" sticker. Price is the most prominent line in   */
/* each column so it's legible at a glance.                               */
/* ---------------------------------------------------------------------- */
.packages-grid {
  display: grid;
  gap: 20px;
  margin-top: var(--sp-2);
}
.package-card {
  display: flex;
  flex-direction: column;
  position: relative;
  min-width: 0;
  padding: clamp(26px, 2.6vw, 38px);
  padding-bottom: 116px;
  background: rgba(255, 255, 255, 0.86);
  border: 1px solid rgba(18, 24, 34, 0.14);
  border-radius: 18px;
  box-shadow: 0 18px 50px rgba(18, 24, 34, 0.055);
  transition: transform 0.24s ease, border-color 0.24s ease, box-shadow 0.24s ease;
}
.package-card:hover {
  transform: translateY(-4px);
  border-color: rgba(18, 24, 34, 0.22);
  box-shadow: 0 24px 58px rgba(18, 24, 34, 0.085);
}
.package-card--popular {
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(246, 248, 253, 0.96) 100%);
  border-color: rgba(10, 78, 255, 0.42);
  box-shadow: 0 22px 54px rgba(18, 24, 34, 0.075);
}
.package-card--popular:hover {
  border-color: rgba(10, 78, 255, 0.62);
  box-shadow: 0 26px 62px rgba(18, 24, 34, 0.1);
}
.package-badge {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  min-height: 28px;
  padding: 5px 11px;
  background: var(--color-accent);
  border-radius: 999px;
  font-size: 0.75rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-white);
  font-weight: 700;
  margin-bottom: var(--sp-3);
}
.package-index {
  font-family: var(--font-display);
  font-size: 1.2rem;
  color: var(--color-accent);
}
.package-head h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.5rem;
  margin-top: var(--sp-2);
}
.package-price {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2.25rem, 2.8vw, 2.85rem);
  color: var(--color-ink);
  margin-top: var(--sp-2);
}
.package-card--popular .package-price { color: var(--color-accent); }
.package-price-prefix {
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-muted);
  margin-right: 6px;
}
.package-body { margin-top: var(--sp-4); }
.package-list-label {
  font-size: 0.76rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted);
  font-weight: 600;
  margin-top: var(--sp-4);
  margin-bottom: var(--sp-1);
}
.package-list-label:first-child { margin-top: 0; }
.package-list {
  border-top: 1px solid var(--color-line);
}
.package-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-line);
  font-size: 0.94rem;
  line-height: 1.5;
  color: var(--color-ink-soft);
}
.package-list li:last-child { border-bottom: none; }
/* Packages content round: the blue checkmark before every "Includes" line
   is removed — a restrained thin-line list (the li's own border-bottom
   already provides the separation) reads calmer than a wall of check
   icons. .package-list--excludes keeps its quieter "–" marker below,
   unchanged; only the checkmark on --includes is removed. */
.package-list--excludes li {
  color: var(--color-muted);
}
.package-list--excludes li::before {
  content: "–";
  color: var(--color-muted);
  flex-shrink: 0;
}
.package-cta {
  position: absolute;
  left: clamp(26px, 2.6vw, 38px);
  right: clamp(26px, 2.6vw, 38px);
  bottom: clamp(26px, 2.6vw, 38px);
  width: auto;
  justify-content: center;
  margin-top: 0;
  padding-top: 14px;
  padding-bottom: 14px;
}
.package-limitation {
  margin-top: var(--sp-3);
  font-size: 0.82rem;
  line-height: 1.55;
  color: var(--color-muted);
}
.scope-details {
  margin-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
}
.scope-details summary {
  position: relative;
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 10px 28px 10px 0;
  color: var(--color-ink-soft);
  font-size: 0.88rem;
  font-weight: 650;
  cursor: pointer;
  list-style: none;
}
.scope-details summary::-webkit-details-marker { display: none; }
.scope-details summary::after {
  content: "+";
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-accent);
  font-size: 1.1rem;
  font-weight: 400;
}
.scope-details[open] summary::after { content: "−"; }
.scope-details p {
  padding: 0 0 var(--sp-2);
  color: var(--color-muted);
  font-size: 0.86rem;
  line-height: 1.6;
}

/* RM500's fixed, always-visible one-line note (Round 4.2) — sits between
   Includes and the collapsed exclusions, so a visitor can never mistake
   RM500 for a fully custom build even without opening the details. */
.package-fixed-note {
  margin-top: var(--sp-3);
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--color-muted);
}

/* RM500's "does not include" list — collapsed by default via native
   <details>, so it doesn't inflate the card's height on first view.
   Native <details>/<summary>: works with no JS, Enter/Space toggle for
   free, and :focus-visible (global rule) lands on <summary> automatically. */
.package-exclusions {
  margin-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
}
.package-exclusions summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 10px 2px;
  cursor: pointer;
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--color-ink-soft);
  list-style: none;
}
.package-exclusions summary::-webkit-details-marker { display: none; }
.package-exclusions summary .icon {
  position: relative;
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}
.package-exclusions summary .icon::before,
.package-exclusions summary .icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--color-ink-soft);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.package-exclusions summary .icon::before {
  width: 12px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}
.package-exclusions summary .icon::after {
  width: 1.5px;
  height: 12px;
  transform: translate(-50%, -50%);
}
.package-exclusions[open] summary .icon::after {
  transform: translate(-50%, -50%) scaleY(0);
  opacity: 0;
}
.package-exclusions .package-list { margin-top: 2px; }

@media (min-width: 900px) {
  .packages-grid {
    grid-template-columns: repeat(3, 1fr);
    gap: 22px;
    align-items: start;
  }
  .package-card { min-height: 730px; }

  /* Round 4.2 — explicit visual placement, deliberately NOT the `order`
     property (see the HTML comment above .packages-grid for why): DOM
     order stays RM899 -> RM500 -> RM1,500, and Tab order always follows
     DOM order regardless of any CSS positioning method, on every
     viewport. Only the desktop paint position is repositioned here, to
     RM500 / RM899 / RM1,500 left-to-right — this intentionally leaves
     desktop's Tab order (RM899 -> RM500 -> RM1,500) different from its
     visual column order (RM500 | RM899 | RM1,500); that divergence is
     accepted, not eliminated by avoiding `order`. (Price Adjustment
     round: package id/price renamed from 888 to 899; the mechanism and
     measurements below are otherwise unchanged from Round 4.2.) */
  /* grid-row: 1 is required here, not optional: with a definite
     grid-column but automatic grid-row, an item whose DOM position would
     otherwise auto-place it into a later row (per the CSS Grid
     auto-placement cursor, which advances through DOM order) can land in
     a different row than items placed earlier/later in source order —
     confirmed by measuring rendered card offsets, where RM899 (first in
     DOM) landed ~750px above RM500/RM1,500 without this. Pinning all
     three to row 1 explicitly removes the ambiguity. */
  .package-card[data-pkg="500"]  { grid-column: 1; grid-row: 1; }
  .package-card[data-pkg="899"]  { grid-column: 2; grid-row: 1; }
  .package-card[data-pkg="1500"] { grid-column: 3; grid-row: 1; }
}

@media (min-width: 900px) and (max-width: 1199px) {
  .package-card { min-height: 810px; }
}

@media (max-width: 899px) {
  .package-card { padding: 28px 24px 108px; }
  .package-cta { left: 24px; right: 24px; bottom: 28px; }
  .package-card:hover { transform: none; }
}

/* ---------------------------------------------------------------------- */
/* Renewal — a lighter supplement to Packages, not a competing pricing     */
/* block. Two modest cards on the section's stone background.             */
/* ---------------------------------------------------------------------- */
.renewal-grid {
  display: grid;
  gap: 18px;
  margin-top: var(--sp-2);
}
.renewal-card {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(18, 24, 34, 0.12);
  border-radius: 18px;
  padding: clamp(26px, 3vw, 40px);
  box-shadow: 0 12px 34px rgba(18, 24, 34, 0.035);
  transition: transform 0.24s ease, border-color 0.24s ease, box-shadow 0.24s ease;
}
.renewal-card:hover {
  transform: translateY(-3px);
  border-color: rgba(18, 24, 34, 0.2);
  box-shadow: 0 20px 48px rgba(18, 24, 34, 0.07);
}
.renewal-card h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.25rem;
}
.renewal-price {
  font-family: var(--font-display);
  font-size: clamp(2rem, 2.7vw, 2.55rem);
  color: var(--color-ink);
  margin-top: var(--sp-1);
}
.renewal-price span {
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-muted);
}
.renewal-list { margin-top: var(--sp-3); }
.renewal-list li {
  font-size: 0.9rem;
  color: var(--color-ink-soft);
  padding: 8px 0;
  border-bottom: 1px solid var(--color-stone-line);
  line-height: 1.5;
}
.renewal-list li:last-child { border-bottom: none; }
@media (min-width: 768px) {
  .renewal-grid { grid-template-columns: 1fr 1fr; gap: 22px; }
}

@media (max-width: 767px) {
  .renewal-card:hover { transform: none; }
}

/* ---------------------------------------------------------------------- */
/* Process — a horizontal, numbered progression on desktop; a clean       */
/* numbered list on mobile. Same markup, no horizontal scroll either way. */
/* Round 4.2: 4 steps (was 5) — .process-track grid-template-columns      */
/* below is repeat(4, 1fr), not 5.                                        */
/* ---------------------------------------------------------------------- */
.process-track {
  display: grid;
  grid-template-columns: 1fr;
  border-top: 1px solid var(--color-line);
}
.process-step {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--color-line);
}
.process-index {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--color-accent);
}
.process-body h4 { font-size: 1.05rem; font-weight: 700; margin-bottom: 6px; }
.process-body p { color: var(--color-muted); font-size: 0.96rem; line-height: 1.55; }

@media (min-width: 900px) {
  .process-track {
    grid-template-columns: repeat(4, 1fr);
    border-top: none;
    gap: var(--sp-3);
    position: relative;
    padding-top: 44px;
  }
  .process-track::before {
    content: "";
    position: absolute;
    top: 20px;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--color-line);
  }
  .process-step {
    display: block;
    border-bottom: none;
    padding: 0;
  }
  .process-index {
    position: relative;
    z-index: 1;
    display: inline-block;
    background: var(--color-paper);
    padding-right: var(--sp-2);
    font-size: 1.7rem;
  }
  .process-body { margin-top: var(--sp-2); }
}

/* ---------------------------------------------------------------------- */
/* Working Responsibilities — two columns, one hairline divider           */
/* ---------------------------------------------------------------------- */
.resp-split {
  display: grid;
  gap: var(--sp-4);
}
.resp-col h4 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.2rem;
  margin-bottom: var(--sp-2);
}
.resp-col ul li {
  font-size: 0.98rem;
  color: var(--color-ink-soft);
  padding: 10px 0;
  border-bottom: 1px solid var(--color-line);
}
.resp-col ul li:last-child { border-bottom: none; }
@media (min-width: 768px) {
  .resp-split {
    grid-template-columns: 1fr 1px 1fr;
    gap: var(--sp-6);
    align-items: start;
  }
  .resp-divider { background: var(--color-line); align-self: stretch; }
}

/* ---------------------------------------------------------------------- */
/* FAQ                                                                    */
/* ---------------------------------------------------------------------- */
.faq-list {
  border-top: 1px solid var(--color-line);
  max-width: 78ch;
}
.faq-item {
  border-bottom: 1px solid var(--color-line);
}
.faq-question {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  background: transparent;
  border: none;
  text-align: left;
  padding: var(--sp-3) 2px;
  cursor: pointer;
  font-weight: 600;
  font-size: 1.05rem;
  color: var(--color-ink);
}
.faq-question .icon {
  position: relative;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
}
.faq-question .icon::before,
.faq-question .icon::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  background: var(--color-ink);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.faq-question .icon::before {
  width: 14px;
  height: 1.5px;
  transform: translate(-50%, -50%);
}
.faq-question .icon::after {
  width: 1.5px;
  height: 14px;
  transform: translate(-50%, -50%);
}
.faq-question[aria-expanded="true"] .icon::after { transform: translate(-50%, -50%) scaleY(0); opacity: 0; }
.faq-answer {
  padding: 0 2px var(--sp-3);
  color: var(--color-muted);
  font-size: 0.98rem;
  line-height: 1.65;
  max-width: 68ch;
}

/* ---------------------------------------------------------------------- */
/* Final CTA — a deliberate, singular close                              */
/* Round 4.2.1: tightened from --sp-8 (128px) to --sp-6 (64px) so the gap */
/* above the Footer reads as deliberate, not empty.                      */
/* ---------------------------------------------------------------------- */
.final-cta {
  background: var(--color-ink);
  color: var(--color-white);
  text-align: center;
  padding: var(--sp-6) 0;
}
.final-cta .eyebrow { color: var(--color-accent-on-dark); }
.final-cta h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(1.9rem, 1.4rem + 2vw, 2.75rem);
  max-width: 22ch;
  margin: 0 auto;
}
.final-cta p {
  margin-top: var(--sp-3);
  color: #b7bccb;
  font-size: 1.05rem;
  max-width: 50ch;
  margin-left: auto;
  margin-right: auto;
}
.final-cta .hero-actions { justify-content: center; margin-top: var(--sp-4); }

/* ---------------------------------------------------------------------- */
/* Footer                                                                 */
/* Round 4.2.1: rebuilt as four clear areas — Brand, Contact, Explore,    */
/* Legal — with real Privacy/Terms links (see the six /privacy//terms/    */
/* pages) replacing the old inert placeholders. DOM order is Brand →     */
/* Contact → Explore → Legal on every viewport (mobile requirement),     */
/* and desktop simply lays the same DOM order out as four columns — no   */
/* `order`/grid-column repositioning is used here, so visual order and   */
/* Tab order stay identical at every width, unlike the Packages grid.    */
/* ---------------------------------------------------------------------- */
/* Footer hierarchy & WhatsApp round: desktop footer height target ~320–
   340px (was ~379px — a further ~12-15% cut on top of the prior round's
   compaction), reached by trimming CONTENT (Brand down to one sentence,
   Explore down to 3 links) and tightening the remaining gaps, never by
   shrinking a font size or a link's 44px tap-target height (.footer-col a
   below is untouched, and the new .footer-whatsapp link matches it). See
   README.md for the full before/after measurement table. */
.site-footer {
  background: var(--color-navy);
  border-top: 1px solid var(--color-line-dark);
  /* Generous outer frame, tight inner gaps: once the content itself
     (columns + copyright bar) is trimmed and its internal spacing
     tightened — see the h5/.footer-bottom/.footer-contact-email rules
     below — the footer reads as cramped unless the whitespace *around*
     that block is deliberately kept elevated. This top/bottom padding is
     that breathing room; it is the one place in this round that adds
     space rather than removing it, and is what lands the measured total
     at ~330px on desktop instead of well under the 320-340px target. */
  padding: 64px 0 56px;
  color: #b7bccb;
}
/* Narrower, centered content group at wide viewports — was the sitewide
   --max-width (1200px), which spread the four columns thin. */
.site-footer .container { max-width: 960px; }
.footer-top {
  display: grid;
  gap: var(--sp-4);
}
/* Visual & Interaction round (§9): the 4-column breakpoint moved from
   768px to 860px, and the Contact column got an explicit min-width
   inside its fr track so "WhatsApp · +60 10-331 9636" never wraps.
   Footer hierarchy round: the WhatsApp entry grew from plain text to an
   icon + label + number link, raising the floor from 210px to 290px.
   Wording round: the MS label became "Hubungi di WhatsApp" (natural
   Malay for the entry, per the client's own correction), which at ~299px
   is now the widest of the three languages and slightly overflowed the
   290px floor at every desktop width — re-measured and bumped to 305px,
   the smallest bump that clears it with a few px to spare in all three
   languages at the 860px breakpoint (the tightest point this track is
   ever asked to hold). This is a floor-width adjustment only: still the
   same four tracks (1.2fr / this one / 1fr / 0.9fr), same 860px
   breakpoint, same ~332px measured footer height. Below 860px the layout
   drops to the stacked mobile grid instead of squeezing. */
@media (min-width: 860px) {
  .footer-top { grid-template-columns: 1.2fr minmax(305px, 1fr) 1fr 0.9fr; gap: var(--sp-3); }
}
.footer-brand .brand { display: inline-block; margin-bottom: var(--sp-2); }
/* Brand copy compresses from a headline + separate tagline paragraph into
   one elevated sentence — kept at the old paragraph's font-size (0.94rem)
   so the height saving comes from cutting a whole line, never from
   shrinking type. */
.footer-tagline {
  color: #9aa3b5;
  font-size: 0.94rem;
  line-height: 1.55;
  max-width: 34ch;
  letter-spacing: 0.01em;
}

/* The white logo is only ever used here, on the dark footer — never on a
   light background, where it would render invisible. */
.footer-logo { height: 24px; width: auto; }

.footer-col h5 {
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #7c8598;
  line-height: 1.2;
  /* h1-h4/p are zeroed by the sitewide reset (see reset rule near the top
     of this file) but h5 never was, so it was still carrying the
     browser's ~1.67em default top margin — a good ~20px of invisible
     space above every column label that no earlier round had actually
     closed. Zeroing it here is most of this round's real title-to-link
     tightening (requirement 6), well beyond what trimming margin-bottom
     alone could do. */
  margin-top: 0;
  margin-bottom: 6px;
  font-weight: 600;
}
.footer-col ul li { padding: 0; }
/* Round 4.2.2: the clickable area is the link itself (display: inline-block
   + padding), not just the li's own padding around an inline anchor — the
   previous rule measured at only ~18px tall via getBoundingClientRect(),
   well under the required 44px tap target. line-height + padding here are
   sized to clear 44px at every viewport without changing the font size. */
.footer-col a {
  display: inline-block;
  padding: 10px 0;
  line-height: 24px;
  text-decoration: none;
  color: #c9cfdb;
  font-size: 0.95rem;
  border-bottom: 1px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.footer-col a:hover,
.footer-col a:focus-visible {
  color: var(--color-white);
}

/* WhatsApp contact entry: icon + label + number on one line, the whole
   thing a single link — not a filled button. No background, no border,
   no shadow; just white (vs. the plain links' muted #c9cfdb) and a
   slightly bolder label to read as the primary contact method. Padding
   and line-height match .footer-col a exactly, so its tap target is the
   same 44px rather than a bespoke bigger control. */
.footer-whatsapp {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 0;
  line-height: 24px;
  text-decoration: none;
  color: var(--color-white);
  font-size: 0.95rem;
  border-bottom: 1px solid transparent;
  white-space: nowrap;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.footer-whatsapp:hover,
.footer-whatsapp:focus-visible {
  border-bottom-color: var(--color-accent-on-dark);
}
.footer-whatsapp-icon {
  width: 17px;
  height: 17px;
  flex: none;
  color: #3fbf6f;
}
.footer-whatsapp-label { font-weight: 600; }
.footer-whatsapp-number { font-weight: 400; color: #c9cfdb; }
/* Full wording ("Chat on WhatsApp" / "WhatsApp 咨询" / "Hubungi di WhatsApp")
   is what every other viewport shows; only the narrowest phones (see the
   max-width breakpoint below) swap it for the bare "WhatsApp" word so the
   full +60 10-331 9636 number never wraps or gets clipped. The number
   itself, and the rest of the link, are never shortened. */
.footer-whatsapp-label-short { display: none; }

/* Email sits immediately under WhatsApp; a negative margin on the <li>
   pulls it visually closer to the entry above it without touching either
   link's own padded 44px tap target. */
.footer-contact-email { margin-top: -10px; }

.footer-bottom {
  margin-top: 14px;
  padding-top: 8px;
  border-top: 1px solid var(--color-line-dark);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
  justify-content: space-between;
  font-size: 0.85rem;
  color: #7c8598;
}
.footer-lang-switch { display: flex; }
/* Footer language-switcher visual-QA fix: the shared .lang-switch a[aria-current]
   rule (above) sets color: var(--color-ink) (#14171c) — on the Footer's
   --color-navy (#12171e) background that is ~1:1 contrast, i.e. invisible;
   only the accent underline showed. Scoped to .site-footer only (Header's own
   .lang-switch, .hero and .final-cta are untouched) and built from existing
   dark-background text tokens: --color-muted-dark for the non-current EN/中文/
   BM items (8.15:1 on navy) and --color-white for the current one (17.99:1),
   still paired with the existing accent underline. No layout, size, spacing
   or link-target change. */
.site-footer .lang-switch a {
  color: var(--color-muted-dark);
  /* Invisible hit-slop, not visible padding: the link's own box (font-size
     0.86rem + 4px/1px padding) measures ~32px tall, under the 44px tap
     target minimum, but visibly enlarging it would grow the footer-bottom
     row and disturb the tuned ~332px desktop / stacked-mobile footer
     height. A ::before extends the CLICKABLE area only, via absolute
     positioning that takes it out of flow — nothing here affects layout,
     size or spacing. 6px each side keeps the extended zones of adjacent
     EN/中文/BM links (separated by 10px gap + the "|" .lang-sep, ~28px+
     edge-to-edge) from overlapping each other. */
  position: relative;
}
.site-footer .lang-switch a::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: max(44px, 100% + 12px);
  height: 44px;
  transform: translate(-50%, -50%);
}
.site-footer .lang-switch a:hover,
.site-footer .lang-switch a:focus-visible {
  color: var(--color-white);
}
.site-footer .lang-switch a[aria-current="page"] {
  color: var(--color-white);
  border-bottom-color: var(--color-accent-on-dark);
}

/* ---------------------------------------------------------------------- */
/* Footer — mobile compression (Round 4.2.2 hotfix; re-tightened in the   */
/* Footer real-compaction round — see the block comment above            */
/* .site-footer). Brand and Contact stay full width (per the pre-launch  */
/* review); Explore and Legal sit side by side as two columns below the  */
/* 860px 4-column breakpoint (raised from 768px in the Visual &          */
/* Interaction round's §9 WhatsApp-single-line fix — see the             */
/* .footer-top rule above) — tested at 320px specifically (Services /    */
/* Selected Work / Packages / Process / FAQ and Privacy Policy / Terms   */
/* of Service all wrap cleanly onto 1–2 lines with no overlap or broken  */
/* layout at either 320 or 375px), so there is no separate 320px         */
/* single-column fallback. Font sizes are untouched; every link's        */
/* clickable area stays >= 44px tall (see .footer-col a above) — only    */
/* the padding/gaps around that fixed-height content were reduced.       */
/* ---------------------------------------------------------------------- */
@media (max-width: 859px) {
  .footer-top {
    grid-template-columns: 1fr 1fr;
    column-gap: 20px;
    row-gap: 12px;
  }
  /* Brand (1st) and Contact (2nd) each span both columns; Explore (3rd)
     and Legal (4th) fall into the two columns of the next grid row. */
  .footer-top > .footer-brand,
  .footer-top > .footer-col:nth-child(2) {
    grid-column: 1 / -1;
  }
  .footer-col h5 { margin-bottom: 6px; }
  .site-footer { padding: 20px 0; }
  .footer-bottom { margin-top: 12px; }
}

/* Narrow phones (320–~420px): copyright + language switcher no longer fit
   one row in any of the three languages without shrinking type (measured —
   the longest pairing, MS, needs ~433px). Rather than let that be an
   incidental flex-wrap side effect, this makes it a deliberate, tested
   layout: both rows are explicitly stacked and left-aligned with a
   compact, fixed gap between them, and the WhatsApp entry's label swaps
   to the bare "WhatsApp" word (defined above) so the full phone number
   stays on one line and never breaks mid-digit. Font sizes and every
   link's 44px tap target are untouched. */
@media (max-width: 479px) {
  .footer-whatsapp-label-full { display: none; }
  .footer-whatsapp-label-short { display: inline; }
  .footer-bottom {
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    gap: 10px;
  }
}

/* ---------------------------------------------------------------------- */
/* Legal pages (Round 4.2.1) — Privacy Policy / Terms of Service.        */
/* Shares css/styles.css, assets/, logo, favicon, colour and type system */
/* with the main site; nothing is duplicated into /privacy/ or /terms/.  */
/* A lighter header than .site-header (no desktop nav / mobile menu —    */
/* just the logo, a back link and the language switcher, always visible  */
/* at every width since there is nothing here that needs a hamburger).   */
/* ---------------------------------------------------------------------- */
.legal-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  height: auto;
  padding-top: 14px;
  padding-bottom: 14px;
}
.legal-header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.legal-back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  color: var(--color-ink-soft);
  font-size: 0.92rem;
  font-weight: 600;
  border-bottom: 1px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.legal-back-link:hover,
.legal-back-link:focus-visible {
  color: var(--color-ink);
  border-bottom-color: var(--color-accent);
}
.legal-lang-switch { display: flex; }

.legal-main { background: var(--color-paper); }
.legal-container {
  max-width: 800px;
  margin: 0 auto;
  padding: var(--sp-6) 24px var(--sp-8);
}
.legal-intro {
  border-bottom: 1px solid var(--color-line);
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-5);
}
.legal-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: clamp(2rem, 1.6rem + 1.6vw, 2.75rem);
  margin-top: var(--sp-2);
  line-height: 1.15;
}
.legal-effective-date {
  margin-top: var(--sp-2);
  color: var(--color-muted);
  font-size: 0.95rem;
}
.legal-body { max-width: 72ch; }
.legal-section + .legal-section { margin-top: var(--sp-5); }
.legal-section h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.3rem;
  margin-bottom: var(--sp-2);
  color: var(--color-ink);
}
.legal-section p {
  color: var(--color-ink-soft);
  font-size: 1rem;
  line-height: 1.75;
}
.legal-section p + p { margin-top: var(--sp-2); }
.legal-section ul {
  margin-top: var(--sp-2);
  display: grid;
  gap: 9px;
}
.legal-section ul li {
  padding-left: 20px;
  position: relative;
  color: var(--color-ink-soft);
  line-height: 1.6;
}
.legal-section ul li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.7em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-accent);
}
.legal-section a { color: var(--color-accent); text-decoration: underline; text-underline-offset: 2px; }

/* ---------------------------------------------------------------------- */
/* Pixel website consultant                                               */
/* ---------------------------------------------------------------------- */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.ai-assistant {
  position: fixed;
  right: max(20px, env(safe-area-inset-right));
  bottom: max(20px, env(safe-area-inset-bottom));
  z-index: 180;
  font-family: var(--font-body);
}
.ai-launcher {
  position: relative;
  display: grid;
  place-items: center;
  width: clamp(106px, 6.5vw, 124px);
  height: clamp(106px, 6.5vw, 124px);
  padding: 0;
  border: 0;
  border-radius: 30px;
  background: transparent;
  box-shadow: none;
  cursor: pointer;
  transition: transform 0.24s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.ai-launcher:hover {
  transform: translateY(-4px) scale(1.045);
}
.ai-launcher:focus-visible {
  outline: 3px solid rgba(10, 78, 255, 0.38);
  outline-offset: 3px;
}
.ai-launcher img {
  display: block;
  width: calc(100% - 2px);
  height: calc(100% - 2px);
  object-fit: contain;
  filter: drop-shadow(0 12px 15px rgba(15, 24, 40, 0.22));
  transform-origin: 50% 78%;
  animation: pixel-idle 3s ease-in-out infinite;
}
.ai-launcher span {
  position: absolute;
  right: calc(100% - 22px);
  bottom: 25px;
  width: max-content;
  padding: 7px 11px;
  border: 1px solid rgba(18, 24, 34, 0.1);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.98);
  color: var(--color-ink);
  font-size: 0.8rem;
  font-weight: 700;
  box-shadow: 0 8px 24px rgba(15, 24, 40, 0.1);
}
.ai-assistant.is-open .ai-launcher { visibility: hidden; }
.ai-panel {
  position: absolute;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  width: min(390px, calc(100vw - 32px));
  height: min(590px, calc(100dvh - 40px));
  overflow: hidden;
  border: 1px solid rgba(18, 24, 34, 0.13);
  border-radius: 22px;
  background: #fff;
  color: var(--color-ink);
  box-shadow: 0 28px 80px rgba(10, 18, 32, 0.24);
}
.ai-panel[hidden] { display: none; }
.ai-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 18px 16px 20px;
  border-bottom: 1px solid var(--color-line);
  background: #fbfcff;
}
.ai-header h2 {
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 750;
}
.ai-header p {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: 3px;
  color: var(--color-muted);
  font-size: 0.78rem;
}
.ai-header p span {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #22a06b;
  box-shadow: 0 0 0 3px rgba(34, 160, 107, 0.12);
}
.ai-close {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: var(--color-ink-soft);
  font-size: 1.65rem;
  line-height: 1;
  cursor: pointer;
}
.ai-close:hover { background: var(--color-stone); }
.ai-messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 20px;
  background: linear-gradient(180deg, #fff 0%, #fafbfe 100%);
}
.ai-message {
  max-width: 88%;
  padding: 11px 14px;
  border-radius: 16px;
  font-size: 0.93rem;
  line-height: 1.52;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.ai-message--assistant {
  align-self: flex-start;
  border: 1px solid var(--color-line);
  border-bottom-left-radius: 5px;
  background: #fff;
  color: var(--color-ink-soft);
}
.ai-message--user {
  align-self: flex-end;
  border-bottom-right-radius: 5px;
  background: var(--color-accent);
  color: #fff;
}
.ai-message.is-thinking { color: var(--color-muted); }
.ai-whatsapp {
  align-self: flex-start;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 10px 14px;
  border-radius: 12px;
  background: #147a50;
  color: #fff;
  font-size: 0.88rem;
  font-weight: 700;
}
.ai-suggestions {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 10px 16px 8px;
  border-top: 1px solid var(--color-line);
  scrollbar-width: none;
}
.ai-suggestions::-webkit-scrollbar { display: none; }
.ai-suggestions button {
  min-height: 38px;
  flex: 0 0 auto;
  padding: 8px 11px;
  border: 1px solid rgba(10, 78, 255, 0.2);
  border-radius: 999px;
  background: #f6f8ff;
  color: #1747c7;
  font-size: 0.78rem;
  font-weight: 600;
  cursor: pointer;
}
.ai-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: 8px;
  padding: 10px 12px 8px;
  background: #fff;
}
.ai-form textarea {
  width: 100%;
  min-height: 46px;
  max-height: 110px;
  resize: none;
  padding: 12px 13px;
  border: 1px solid rgba(18, 24, 34, 0.18);
  border-radius: 13px;
  background: #fff;
  color: var(--color-ink);
  font: inherit;
  font-size: 0.92rem;
  line-height: 1.4;
}
.ai-form textarea:focus {
  outline: 2px solid rgba(10, 78, 255, 0.22);
  border-color: var(--color-accent);
}
.ai-form button {
  min-width: 68px;
  min-height: 46px;
  padding: 10px 14px;
  border: 0;
  border-radius: 13px;
  background: var(--color-accent);
  color: #fff;
  font-weight: 700;
  cursor: pointer;
}
.ai-form button:disabled { opacity: 0.55; cursor: wait; }
.ai-privacy {
  padding: 0 14px 12px;
  background: #fff;
  color: var(--color-muted);
  font-size: 0.7rem;
  line-height: 1.35;
  text-align: center;
}
@media (max-width: 479px) {
  .ai-assistant {
    right: max(12px, env(safe-area-inset-right));
    bottom: max(12px, env(safe-area-inset-bottom));
  }
  .ai-launcher { width: 92px; height: 92px; border-radius: 28px; }
  .ai-launcher img { width: 90px; height: 90px; }
  .ai-launcher span { display: none; }
  .ai-panel {
    width: calc(100vw - 24px);
    height: min(620px, calc(100dvh - 24px));
    border-radius: 20px;
  }
}
@media (max-width: 479px) and (max-height: 650px) {
  .ai-launcher { width: 78px; height: 78px; }
  .ai-launcher img { width: 76px; height: 76px; }
}
@media (prefers-reduced-motion: reduce) {
  .ai-launcher { transition: none; }
  .ai-launcher img,
  .ai-assistant.is-dancing .ai-launcher img { animation: none !important; }
}
.ai-assistant.is-dancing .ai-launcher img {
  animation: pixel-dance 1.2s cubic-bezier(0.3, 0.8, 0.3, 1) both;
}
@keyframes pixel-idle {
  0%, 100% {
    transform: translateY(0) rotate(-0.5deg);
    filter: drop-shadow(0 12px 15px rgba(15, 24, 40, 0.2));
  }
  50% {
    transform: translateY(-8px) rotate(0.7deg);
    filter: drop-shadow(0 17px 19px rgba(10, 78, 255, 0.26));
  }
}
@keyframes pixel-dance {
  0%, 100% { transform: translateY(0) rotate(0) scale(1); }
  14% { transform: translateY(-9px) rotate(-7deg) scale(1.03, 0.97); }
  29% { transform: translateY(1px) rotate(6deg) scale(0.98, 1.02); }
  44% { transform: translateY(-13px) rotate(-5deg) scale(1.04, 0.96); }
  60% { transform: translateY(0) rotate(5deg) scale(0.98, 1.02); }
  76% { transform: translateY(-7px) rotate(-3deg) scale(1.02, 0.98); }
  88% { transform: translateY(0) rotate(2deg) scale(1); }
}

/* Images remain viewable and clickable, while ordinary browser save/drag
   callouts are suppressed. Text selection elsewhere stays untouched. */
img {
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  user-select: none;
}

main section[id], main article[id], .site-footer[id] {
  scroll-margin-top: calc(var(--header-height, 76px) + 12px);
}
.legal-section a:hover { color: var(--color-accent-dark); }
.legal-closing-note {
  margin-top: var(--sp-3);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--color-line);
  color: var(--color-muted);
  font-size: 0.92rem;
}

@media (max-width: 480px) {
  .legal-container { padding: var(--sp-5) 20px var(--sp-6); }
}

/* ---------------------------------------------------------------------- */
/* Utility                                                                */
/* ---------------------------------------------------------------------- */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
