/* ============================================================
   styles.css — think(auto) marketing site (Malaysia).
   Reads brand values from design-tokens.css, which every page links in its
   <head> BEFORE this file. There is deliberately no @import here: an @import
   is only discovered once this file has downloaded, which serialises two
   requests that the two <link>s let the browser make at once.
   Minimal aesthetic: white canvas, one blue accent, generous space.
   Signature: the think(auto) parenthesis / function-call motif.

   Descended from the German site's stylesheet in ../website-upload/, and
   deliberately kept close to it — same tokens, same components, same type
   scale. What is gone: the language toggle, the DSGVO Art. 21(4) callout,
   and the German line on the 404. What is new: .stages / .boundary for the
   three-stage system section, and .gate / .booking__embed for the Calendly
   consent gate.
   ============================================================ */

/* ---------- Reset / base ---------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font-body);
  font-weight: var(--fw-body);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--color-ink);
  font-weight: var(--fw-black);
  letter-spacing: -0.02em;
}
/* Display headings sit tight; the small ones wrap and need room to do it. */
h1, h2 { line-height: 1.1; }
h3, h4 { line-height: 1.25; }

/* Every section title is an h2, so the size belongs here rather than on each
   section. Without it they fell back to the UA default 2em — which left the
   section titles smaller than the case-study h3s nested underneath them. */
h2 { font-size: var(--text-3xl); }

/* `* { margin: 0 }` above wipes the UA heading margins, so the gap under a
   heading has to be stated. Stating it on the heading (rather than as
   margin-top on whatever follows) means one rule per heading rank instead of
   one per following element — and a section that forgets it can't end up with
   its first paragraph jammed against the title. */
.section-head h2,
.about__body h2,
.founder__body h2,
.cta__inner h2,
.legal h1,
.notfound h1 { margin-bottom: var(--space-3); }

img, svg { max-width: 100%; display: block; }
a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-dark); }

:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 4px; }

::selection { background: var(--color-primary); color: #fff; }

/* ---------- Layout ---------- */
.container {
  width: 100%;
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.section { padding-block: clamp(2.5rem, 5vw, 4rem); }
/* The header is sticky and 72px tall, so a jump to #system or #contact would
   otherwise land with the section title tucked underneath it. */
[id] { scroll-margin-top: 88px; }
.section--alt { background: var(--color-bg-alt); }
.section--dark { background: var(--color-bg-dark); }
.section--dark h1, .section--dark h2, .section--dark h3 { color: #fff; }
/* The muted grey a .lead uses on white is only 3.7:1 on ink — under AA. Lift it
   to the same value the dark section already uses for its card copy (9.2:1). */
.section--dark .lead { color: rgba(255,255,255,.72); }

/* Blue parenthetical accent for use inside headings */
.paren { color: var(--color-primary); white-space: nowrap; }

.section-head { max-width: 42rem; margin-bottom: var(--space-5); }

/* Lead — the paragraph directly under a title. One treatment site-wide, so the
   hero, every section head, the contact CTA and the 404 page all read the same.
   Spacing above it comes from the heading's margin-bottom. */
.lead {
  font-size: var(--text-lg);
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: .6ch;
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  font-size: var(--text-base);
  line-height: 1;
  padding: 0.95em 1.4em;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  transition: transform var(--transition), background var(--transition), box-shadow var(--transition), color var(--transition);
  white-space: nowrap;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }

.btn--primary { background: var(--color-primary); color: #fff; box-shadow: 0 8px 20px -8px rgba(37,99,235,.6); }
.btn--primary:hover { background: var(--color-primary-dark); color: #fff; }

.btn--ghost { background: transparent; color: var(--color-ink); border-color: var(--color-border); }
.btn--ghost:hover { border-color: var(--color-ink); color: var(--color-ink); }

.btn .arrow { transition: transform var(--transition); }
.btn:hover .arrow { transform: translateX(3px); }

/* ---------- Header / nav ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255,255,255,.85);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--color-border);   /* always-on barrier */
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--transition);
}
.site-header.is-scrolled { box-shadow: var(--shadow-md); }

.nav {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  height: 72px;
}
/* Logo PNGs in assets/logo/ are trimmed to the glyph box — spacing is CSS's job. */
.nav__logo img { height: 24px; width: auto; }
.nav__logo { display: flex; align-items: center; }
.nav__mark { display: none; }
.nav__links {
  display: flex;
  gap: var(--space-4);
  margin-left: auto;
  list-style: none;
  padding: 0;
}
.nav__links a {
  color: var(--color-text);
  font-weight: var(--fw-med);
  font-size: var(--text-sm);
  font-family: var(--font-heading);
}
.nav__links a:hover { color: var(--color-primary); }

.nav__actions { display: flex; align-items: center; gap: var(--space-3); }

/* Mobile nav */
.nav__toggle { display: none; background: none; border: 0; cursor: pointer; padding: .4em; }
.nav__toggle span { display: block; width: 22px; height: 2px; background: var(--color-ink); margin: 4px 0; transition: var(--transition); }

@media (max-width: 860px) {
  /* The links collapse into the hamburger; the CTA does not. Getting an enquiry
     is the page's one job, so the button stays on screen under the sticky header
     instead of living at the bottom of the page. */
  .nav__links { display: none; }
  .nav__actions { margin-left: auto; }  /* .nav__links carried this; it's hidden now */
  .nav .btn--primary { padding: .7em 1em; font-size: var(--text-sm); white-space: nowrap; }
  .nav__toggle { display: block; }
  .nav__links.is-open {
    display: flex;
    flex-direction: column;
    position: absolute;
    top: 72px; left: 0; right: 0;
    background: #fff;
    border-bottom: 1px solid var(--color-border);
    padding: var(--space-4);
    gap: var(--space-3);
    box-shadow: var(--shadow-md);
  }
}

/* Phone widths: the horizontal lockup is ~139px and the bar runs out of room
   once logo + booking button + menu all have to fit. The mark alone is ~28px,
   which is what keeps the CTA on screen at 360px. */
@media (max-width: 560px) {
  .nav { gap: var(--space-2); }
  .nav__lockup { display: none; }
  .nav__mark { display: block; height: 28px; width: 28px; }
  .nav__actions { gap: var(--space-2); }
  .nav .btn--primary { padding: .6em .8em; font-size: var(--text-xs); }
}

/* ---------- Hero ---------- */
.hero { position: relative; padding-block: clamp(2.75rem, 6vw, 4.75rem); overflow: hidden; }
.hero__grid {
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: var(--space-6);
  align-items: center;
}
.hero__title {
  font-size: var(--text-4xl);
  letter-spacing: -0.03em;
  margin-bottom: var(--space-4);   /* bigger heading, bigger gap than a section title */
}
/* The h1 carries a hard break so the two halves of the sentence land where they
   read best on a wide screen. The threshold is the width of the longer half at
   the capped 3.1rem size — about 550px of text column, which the two-column hero
   only reaches from roughly 1030px of viewport up. Below that the break stops
   helping and starts stranding "should" on a line of its own, so it is dropped
   and the headline wraps on its own. The source keeps a real space before the
   <br> precisely so the two halves don't run together once it is hidden.
   .paren keeps "(itself)" intact either way. */
@media (max-width: 1080px) { .hero__break { display: none; } }

/* A lead, but hero-width: narrower measure against the tall h1 above it. */
.hero__subtitle { max-width: 34rem; }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: var(--space-5); }
/* Small supporting chips — deliberately quieter than the buttons above them. */
.hero__trust { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: var(--space-4); padding: 0; }
.hero__trust li {
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: .5ch;
  font-family: var(--font-heading);
  font-weight: var(--fw-med);
  font-size: var(--text-xs);
  letter-spacing: .01em;
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  background: #fff;
  border-radius: 999px;
  padding: .35em .8em;
}
.hero__trust li::before {
  content: "";
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--color-primary);
  flex: 0 0 auto;
}

/* Hero signature: automation flow diagram */
.hero__visual { position: relative; }
.flow { width: 100%; height: auto; }
.flow .node { fill: #fff; stroke: var(--color-border); }
.flow .node-accent { fill: var(--color-primary-soft); stroke: var(--color-primary); }
.flow .edge { stroke: var(--color-border); stroke-width: 2; fill: none; }
.flow .pulse { stroke: var(--color-primary); stroke-width: 2.5; fill: none; stroke-linecap: round; }
.flow .chip { fill: var(--color-ink); }
/* The blue parts of the diagram take their colour here, never from a fill=
   attribute in the markup, so the SVG follows the tokens like everything else. */
.flow .chip-accent,
.flow .label { fill: var(--color-primary); }
.flow .pulse-anim { stroke-dasharray: 26 220; animation: flowPulse 2.6s linear infinite; }
@keyframes flowPulse { from { stroke-dashoffset: 246; } to { stroke-dashoffset: 0; } }

@media (max-width: 860px) {
  .hero__grid { grid-template-columns: 1fr; }
  /* Headline and CTA first: on a phone the top of the screen has to answer
     "what is this, why do I care" before it shows the diagram. */
  .hero__visual { max-width: 460px; }
}

/* ---------- Trust bar ---------- */
.trustbar { border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.trustbar__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); padding-block: var(--space-5); }
.trustbar__item { display: flex; gap: var(--space-3); align-items: flex-start; }
/* Same rank as the process steps and the compliance values — same size. */
.trustbar__item h3 { font-size: var(--text-lg); margin-bottom: var(--space-1); }
.trustbar__item p { font-size: var(--text-sm); color: var(--color-text-muted); }
.trustbar__icon { color: var(--color-primary); flex: 0 0 auto; margin-top: 2px; }
@media (max-width: 720px) { .trustbar__grid { grid-template-columns: 1fr; gap: var(--space-4); } }

/* ---------- The system: three stages ----------
   A numbered list, stacked rather than in a grid, because the three stages are
   sequential and a row of equal cards would read as a menu to pick from. The
   connecting rule down the left is what carries the order visually; it stops at
   the last item, so the sequence has a visible end. */
.stages { list-style: none; padding: 0; margin: 0; }
.stage {
  position: relative;
  display: grid;
  grid-template-columns: 4.5rem 1fr;
  gap: var(--space-4);
  padding-bottom: var(--space-5);
}
.stage:last-child { padding-bottom: 0; }
/* The rule lives on the item, inset to pass through the centre of the number,
   so nothing has to know how many stages there are. */
.stage:not(:last-child)::before {
  content: "";
  position: absolute;
  left: calc(2.25rem - 1px);
  top: 2.6rem;
  bottom: var(--space-2);
  width: 2px;
  background: var(--color-border);
}
.stage__num {
  position: relative;
  display: grid;
  place-items: center;
  width: 4.5rem;
  height: 2.4rem;
  border: 2px solid var(--color-primary-soft);
  border-radius: 999px;
  background: #fff;
  font-family: var(--font-heading);
  font-weight: var(--fw-black);
  font-size: var(--text-base);
  color: var(--color-primary);
  letter-spacing: .02em;
}
.stage__body h3 { font-size: var(--text-xl); margin-bottom: var(--space-2); }
.stage__body p { font-size: var(--text-base); color: var(--color-text); max-width: 44rem; }

/* "Where it stops" — the section's argument, so it gets the ink treatment
   rather than another white card. Naming what we don't build is the claim. */
.boundary {
  margin-top: var(--space-6);
  background: var(--color-bg-dark);
  border-radius: var(--radius-lg);
  padding: clamp(1.25rem, 3vw, 2rem);
  box-shadow: var(--shadow-md);
}
.boundary h3 {
  color: #fff;
  font-size: var(--text-lg);
  margin-bottom: var(--space-2);
}
.boundary h3::before { content: "( "; color: var(--color-primary); }
.boundary h3::after  { content: " )"; color: var(--color-primary); }
.boundary p { font-size: var(--text-sm); color: rgba(255,255,255,.72); max-width: 46rem; }

@media (max-width: 560px) {
  .stage { grid-template-columns: 1fr; gap: var(--space-2); }
  .stage:not(:last-child)::before { display: none; }
  .stage__num { width: 3.2rem; height: 2rem; font-size: var(--text-sm); }
}

/* ---------- Work / case studies ----------
   Each build is a full-width dossier rather than one of two grid cells: the
   material is technical, and a prospect judging engineering rigour needs room
   to read it. No hover lift — the card isn't clickable, so an affordance
   would be a lie. */
.work__list { display: flex; flex-direction: column; gap: var(--space-5); }

.case {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: #fff;
  padding: clamp(1.25rem, 3vw, 2.25rem);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* Signature line — the brand's function-call motif carrying each build's real
   input and output. Scrolls rather than wraps: it's one expression. */
.case__sig {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow-x: auto;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}
.case__fn { color: var(--color-primary); font-weight: 600; }

.case__head {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  align-items: flex-start; justify-content: space-between;
}
.case__title { font-size: var(--text-2xl); line-height: 1.15; margin-bottom: var(--space-1); }
.case__sub { font-size: var(--text-base); color: var(--color-text-muted); }
.case__meta { display: flex; flex-wrap: wrap; gap: .4rem; list-style: none; padding: 0; }
.case__tag, .case__status {
  font-family: var(--font-heading); font-size: var(--text-xs); font-weight: var(--fw-bold);
  letter-spacing: .04em; border-radius: 999px; padding: .35em .85em; white-space: nowrap;
}
.case__tag { background: var(--color-primary-soft); color: var(--color-primary-dark); }
/* Dashed = "not a client deployment". The honesty is part of the credibility. */
.case__status { border: 1px dashed var(--color-border); color: var(--color-text-muted); }

.case__body { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: var(--space-5); align-items: start; }
.case__visual { background: var(--color-bg-alt); border-radius: var(--radius); padding: var(--space-4); }
.case__visual svg { width: 100%; height: auto; }
.case__text { display: flex; flex-direction: column; gap: var(--space-4); }

.case__block h4, .case__honest h4 {
  font-family: var(--font-heading); font-size: var(--text-xs); font-weight: var(--fw-bold);
  text-transform: uppercase; letter-spacing: .08em; color: var(--color-text-muted);
  margin-bottom: var(--space-2);
}
.case__block p { font-size: var(--text-sm); color: var(--color-text); }

.case__honest { border-top: 1px solid var(--color-border); padding-top: var(--space-4); }
/* auto-fit, not a fixed 3, so a case with two principles still fills the row. */
.case__honest ul { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--space-4); }
.case__honest li {
  font-size: var(--text-sm); color: var(--color-text-muted);
  border-left: 2px solid var(--color-primary-soft); padding-left: var(--space-3);
}
.case__honest li strong { color: var(--color-ink); font-weight: var(--fw-bold); }

.case__foot {
  border-top: 1px solid var(--color-border); padding-top: var(--space-4);
}
.case__result {
  font-size: var(--text-base); font-weight: var(--fw-med); color: var(--color-ink);
  display: flex; gap: .6ch; align-items: baseline; max-width: 44rem;
}
.case__result::before { content: "→"; color: var(--color-primary); font-weight: var(--fw-bold); }

/* 860px is the site-wide "two columns become one" breakpoint. */
@media (max-width: 860px) {
  .case__body { grid-template-columns: 1fr; }
  .case__honest ul { grid-template-columns: 1fr; gap: var(--space-3); }
  /* Wrap the signature at the arrows rather than showing a scrollbar and
     cutting the expression off mid-word. */
  .case__sig { white-space: normal; overflow-x: visible; line-height: 1.7; }
}

/* ---------- Process ---------- */
.process__grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4); counter-reset: step; }
.step { position: relative; padding-top: var(--space-4); border-top: 2px solid var(--color-border); }
.step__num { font-family: var(--font-heading); font-weight: var(--fw-black); font-size: var(--text-lg); color: var(--color-primary); margin-bottom: var(--space-2); }
.step__num::before { content: "("; }
.step__num::after { content: ")"; }
.step h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.step p { font-size: var(--text-sm); color: var(--color-text-muted); }
@media (max-width: 860px) { .process__grid { grid-template-columns: repeat(2, 1fr); gap: var(--space-5); } }
@media (max-width: 480px) { .process__grid { grid-template-columns: 1fr; } }

/* ---------- About ----------
   About and Founder are the site's two prose sections; they share a gutter and
   a prose size so the page doesn't change voice between them. */
.about__grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem, 6vw, 4.5rem); align-items: center; }
.about__body p { font-size: var(--text-base); color: var(--color-text); }
.about__body p + p { margin-top: var(--space-3); }
@media (max-width: 860px) { .about__grid { grid-template-columns: 1fr; } }

/* Brand card — the wordmark as a "function call", on ink */
.brandcard {
  background: var(--color-bg-dark);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-lg);
}
.brandcard__logo { width: min(15rem, 62%); height: auto; }
.brandcard__code {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-dark);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.7;
  color: rgba(255,255,255,.62);
  white-space: pre;
  overflow-x: auto;
}
.brandcard__line {
  margin-top: var(--space-4);
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  font-size: var(--text-sm);
  color: #fff;
}

/* ---------- Founder / personal ---------- */
.founder__grid { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: clamp(2rem, 6vw, 4.5rem); align-items: center; }

.founder__media { position: relative; width: 100%; max-width: 23rem; }
/* Offset outline behind the portrait — the one signature flourish here */
.founder__media::before {
  content: "";
  position: absolute;
  inset: var(--space-5) calc(-1 * var(--space-3)) calc(-1 * var(--space-3)) var(--space-5);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-lg);
}
.founder__photo {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-bg-alt);
  box-shadow: var(--shadow-md);
}
.founder__photo img { width: 100%; height: 100%; object-fit: cover; }

.founder__card {
  position: absolute;
  left: calc(-1 * var(--space-3));
  bottom: var(--space-4);
  display: flex;
  align-items: center;
  gap: .75rem;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: .7rem .95rem;
  box-shadow: var(--shadow-md);
  max-width: 92%;
}
.founder__mark { width: 34px; height: 34px; border-radius: 9px; flex: 0 0 auto; }
.founder__card strong {
  display: block;
  font-family: var(--font-heading);
  font-weight: var(--fw-bold);
  font-size: var(--text-sm);
  color: var(--color-ink);
  line-height: 1.3;
}
.founder__card span {
  display: block;
  font-size: var(--text-xs);
  font-weight: var(--fw-med);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--color-text-muted);
}

.founder__body { max-width: 36rem; }
.founder__body p { font-size: var(--text-base); color: var(--color-text); }
.founder__body p + p { margin-top: var(--space-3); }
/* Lead size, but ink and medium: this one is spoken in the first person. */
.founder__lead { font-size: var(--text-lg); line-height: 1.6; color: var(--color-ink); font-weight: var(--fw-med); }

.founder__quote {
  margin: var(--space-4) 0 0;
  padding-left: var(--space-3);
  border-left: 3px solid var(--color-primary);
  font-family: var(--font-heading);
  font-weight: var(--fw-med);
  font-size: var(--text-lg);
  line-height: 1.45;
  color: var(--color-ink);
}

.founder__cta { margin-top: var(--space-5); }

@media (max-width: 860px) {
  .founder__grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .founder__media { max-width: 20rem; }
}
@media (max-width: 480px) {
  .founder__card { left: 0; }
}

/* ---------- Value props ---------- */
.values__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
.value { display: flex; gap: var(--space-3); }
.value__icon { color: var(--color-primary); flex: 0 0 auto; }
.value h3 { font-size: var(--text-lg); margin-bottom: var(--space-2); }
.value p { font-size: var(--text-sm); color: var(--color-text-muted); }
.section--dark .value p { color: rgba(255,255,255,.7); }
@media (max-width: 720px) { .values__grid { grid-template-columns: 1fr; } }

/* ---------- Contact section ---------- */
.cta__inner { text-align: center; max-width: 40rem; margin-inline: auto; margin-bottom: var(--space-6); }

/* Calendar left, form right, and this time both are real doors — the booking
   panel holds a 680px embed once it is loaded, not a paragraph — so both get
   the card treatment. `align-items: start` keeps the shorter one at the top
   instead of centred against the taller. */
.reach__grid {
  display: grid;
  grid-template-columns: 1fr 0.85fr;
  gap: var(--space-5);
  align-items: start;
  max-width: 64rem;
  margin-inline: auto;
}

.booking,
.contact {
  padding: var(--space-5) var(--space-4);
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.panel__icon { color: var(--color-primary); margin-bottom: var(--space-3); }
.booking h3,
.contact h3 { font-size: var(--text-xl); margin-bottom: var(--space-2); }
.booking > p,
.contact > p { font-size: var(--text-sm); color: var(--color-text-muted); }

/* Consent gate for the Calendly embed. Styled as a decision rather than a
   cookie nag: the explanation is at readable body size, and the alternatives
   are named under the button so declining is a real option, not a dead end.
   js/main.js hides this only once the widget script has actually loaded. */
.gate {
  margin-top: var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: #fff;
  padding: var(--space-4);
}
.gate__text { font-size: var(--text-sm); color: var(--color-text); }
.gate__text a,
.gate__alt a { text-decoration: underline; text-underline-offset: 2px; overflow-wrap: anywhere; }
.gate__btn { margin-top: var(--space-4); width: 100%; justify-content: center; }
.gate__btn:disabled { opacity: .7; cursor: default; transform: none; }
.gate__alt { font-size: var(--text-xs); color: var(--color-text-muted); margin-top: var(--space-3); }
.gate__error {
  font-size: var(--text-sm);
  font-weight: var(--fw-med);
  color: var(--color-error);
  background: var(--color-error-soft);
  border-radius: var(--radius);
  padding: .6rem .8rem;
  margin-top: var(--space-3);
}

/* The Calendly iframe sizes itself; this only gives it somewhere to sit that
   matches the panel and clips its corners. */
.booking__embed {
  margin-top: var(--space-4);
  border-radius: var(--radius);
  overflow: hidden;
  background: #fff;
}
.booking__embed .calendly-inline-widget { width: 100%; }

/* One line under both panels — the "you'll hear back" promise, which on the
   German site had its own explainer column to live in. */
.reach__foot {
  max-width: 64rem;
  margin: var(--space-5) auto 0;
  text-align: center;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.reach__foot a { text-decoration: underline; text-underline-offset: 3px; overflow-wrap: anywhere; }

/* ---------- Contact form ---------- */
.contact__form { margin-top: var(--space-4); }

/* Honeypot: off-screen rather than display:none, which too many bots skip. */
.contact__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field + .field { margin-top: var(--space-3); }
.field label {
  display: block;
  font-size: var(--text-sm);
  font-weight: var(--fw-med);
  color: var(--color-ink);
  margin-bottom: var(--space-1);
}
.field__req { color: var(--color-primary); margin-left: .15em; }

.field input,
.field textarea {
  width: 100%;
  font: inherit;
  font-size: var(--text-sm);
  font-weight: var(--fw-body);
  color: var(--color-ink);
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: .65rem .8rem;
  transition: border-color var(--transition);
}
.field textarea { resize: vertical; min-height: 7rem; line-height: 1.55; }
.field input:hover,
.field textarea:hover { border-color: var(--color-border-strong); }
/* border-radius is restated on purpose: the global :focus-visible rule forces 4px,
   which would snap the corners square the moment a field is focused. */
.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: var(--ring);
  border-radius: var(--radius);
}
.field.is-invalid input,
.field.is-invalid textarea { border-color: var(--color-error); }

.contact__submit { margin-top: var(--space-4); width: 100%; justify-content: center; }

/* Empty between submissions — :empty keeps it from reserving space. */
.contact__status {
  font-size: var(--text-sm);
  font-weight: var(--fw-med);
  margin-top: var(--space-3);
  padding: .6rem .8rem;
  border-radius: var(--radius);
}
.contact__status:empty { display: none; }
.contact__status.is-ok  { color: var(--color-success); background: var(--color-success-soft); }
.contact__status.is-err { color: var(--color-error);   background: var(--color-error-soft); }

.contact__note { font-size: var(--text-xs); color: var(--color-text-muted); margin-top: var(--space-3); }
.contact__note a { text-decoration: underline; text-underline-offset: 2px; }

@media (max-width: 900px) {
  .reach__grid { grid-template-columns: 1fr; }
}

/* ---------- contact.php confirmation page (no-JS path) ---------- */
.formresult { max-width: 34rem; margin-inline: auto; text-align: center; padding-block: var(--space-6); }
.formresult__mark {
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto var(--space-4);
  display: grid;
  place-items: center;
  border-radius: 50%;
  font-size: 1.75rem;
  font-weight: var(--fw-bold);
  line-height: 1;
}
.formresult__mark.is-ok  { color: var(--color-success); background: var(--color-success-soft); }
.formresult__mark.is-err { color: var(--color-error);   background: var(--color-error-soft); }
.formresult h1 { font-size: var(--text-3xl); margin-bottom: var(--space-3); }
.formresult__actions { margin-top: var(--space-5); }

/* ---------- Footer ---------- */
.footer { background: var(--color-bg-dark); color: rgba(255,255,255,.72); padding-block: var(--space-7) var(--space-5); }
.footer a { color: rgba(255,255,255,.72); }
.footer a:hover { color: #fff; }
.footer__grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: var(--space-5); }
.footer__logo img { height: 22px; width: auto; margin-bottom: var(--space-3); }
.footer__brandline { font-size: var(--text-sm); max-width: 22rem; }
/* The registered name, SSM number and address. Not legally required of a
   Malaysian sole proprietorship the way a German Impressum is, but a local
   prospect checking whether you are a real registered business will look for
   exactly these three things, and there is no separate imprint page to hold
   them. `font-style: normal` because <address> is italic by UA default. */
.footer__address {
  font-style: normal;
  font-size: var(--text-xs);
  line-height: 1.7;
  color: rgba(255,255,255,.5);
  margin-top: var(--space-4);
}
/* Same micro-label treatment as the case-study block labels (.08em tracking). */
.footer h4 { color: #fff; font-size: var(--text-sm); text-transform: uppercase; letter-spacing: .08em; margin-bottom: var(--space-3); }
.footer ul { list-style: none; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); font-size: var(--text-sm); }
.footer__bottom {
  display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: space-between;
  margin-top: var(--space-6); padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-dark);
  font-size: var(--text-xs); color: rgba(255,255,255,.5);
}
@media (max-width: 860px) { .footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .footer__grid { grid-template-columns: 1fr; } }

/* ---------- Scroll reveal ----------
   Gated on the .js class that the inline <script> in index.html's <head> adds
   before first paint. Without it (JavaScript off, blocked by a proxy, or failing
   before initReveal() runs) the elements are simply visible. Never hide .reveal
   unconditionally: 26 elements carry it, so an unconditional rule leaves a no-JS
   visitor, a crawler or a reader mode with the hero, a blank page and the footer. */
.js .reveal { opacity: 0; transform: translateY(18px); transition: opacity .6s ease, transform .6s ease; }
.js .reveal.is-visible { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  * { scroll-behavior: auto !important; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .btn:hover { transform: none; }
  .flow .pulse-anim { animation: none !important; }
}

/* ---------- Legal page (pdpa-notice.html) ---------- */
.legal { padding-block: clamp(2.5rem, 5vw, 4rem); }
.legal__wrap { max-width: 50rem; margin-inline: auto; }
.legal h1 { font-size: var(--text-3xl); }
/* Headings inside a legal page rank below its title — hence xl, not the
   section-title size the h2 rule sets for the marketing page. */
.legal h2 { font-size: var(--text-xl); margin-top: var(--space-6); margin-bottom: var(--space-2); }
.legal h3 { font-size: var(--text-base); margin-top: var(--space-4); margin-bottom: var(--space-1); }
.legal p, .legal li { color: var(--color-text); font-size: var(--text-base); }
.legal p { margin-bottom: var(--space-3); }
.legal ul { margin: 0 0 var(--space-3) 1.25rem; }
.legal li { margin-bottom: var(--space-1); }
/* Tables carry the disclosure classes and retention periods in pdpa-notice.html.
   The wrapper is what keeps a phone usable: a three-column table scrolls inside its
   own box instead of pushing the whole page sideways. Always wrap, never bare. */
.legal__tablewrap { overflow-x: auto; margin-bottom: var(--space-3); }
.legal table { border-collapse: collapse; width: 100%; min-width: 28rem; }
.legal th,
.legal td { text-align: left; vertical-align: top; padding: var(--space-2); border-bottom: 1px solid var(--color-border); font-size: var(--text-sm); color: var(--color-text); }
.legal th { font-family: var(--font-heading); font-weight: var(--fw-med); color: var(--color-ink); }
/* Orientation note at the top of the PDPA notice. s. 7(3) PDPA makes both the
   Bahasa Malaysia and the English version mandatory, so neither is a translation
   of the other — this box is what points a reader at the one they want. */
.legal__intro {
  border-left: 3px solid var(--color-primary);
  background: var(--color-primary-soft);
  border-radius: 0 6px 6px 0;
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-5);
}
.legal__intro strong { color: var(--color-text); }
/* Marks a value in the legal text that is known to be provisional. Nothing uses
   it today — the PDPA notice states only facts that are true right now, and the
   pending hosting move is recorded in a maintainer comment in its <head> rather
   than shown to readers. Kept because the styling is deliberately loud: a
   placeholder that blends in is a placeholder that ships. */
.placeholder {
  background: var(--color-primary-soft);
  border: 1px dashed var(--color-primary);
  border-radius: 4px;
  padding: 0 .35em;
  color: var(--color-primary-dark);
  font-weight: var(--fw-med);
  font-family: var(--font-heading);
}

/* ---------- 404 ----------
   Two columns: the human sentence on the left, the machine's account of what
   happened on the right. Left-aligned and asymmetric on purpose — the centred
   stack with a giant numeral is the default every 404 reaches for, and it says
   nothing about what this business does. Reading order puts the copy first, so
   a visitor who doesn't care about the conceit gets the way back immediately
   and the card is the payoff rather than the toll. */
/* The 404 is the one page with no footer and too little content to fill a
   screen, so it is centred in what's left below the sticky 72px header rather
   than left hanging at the top of an empty page. min-height, not height: if the
   card and copy ever grow past the viewport the page simply scrolls. */
.notfound-main {
  display: flex;
  align-items: center;
  min-height: calc(100vh - 72px);
}
.notfound {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
  /* Narrower than the 74rem container the rest of the site uses. Two short
     columns stretched to full width read as a gap with text at the edges. */
  max-width: 58rem;
  width: 100%;
  margin-inline: auto;
  padding-block: var(--space-6);
}
.notfound__copy { max-width: 30rem; }
.notfound h1 { font-size: var(--text-3xl); }
.notfound__actions {
  display: flex; flex-wrap: wrap; gap: var(--space-3);
  margin-top: var(--space-5);
}

/* The trace card. Same ink surface and white-opacity ladder as the footer and
   the dark section, so it reads as part of the system rather than a one-off.
   The mono face is the site's utility voice for machine output — it is what the
   parked case-study signature lines use. */
.trace {
  background: var(--color-bg-dark);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--shadow-lg);
}
.trace__head {
  display: flex; align-items: baseline; gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border-dark);
}
.trace__status {
  font-family: var(--font-heading);
  font-weight: var(--fw-black);
  font-size: var(--text-2xl);
  line-height: 1;
  letter-spacing: -0.03em;
  color: #fff;
}
.trace__label {
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  letter-spacing: .06em;
  color: rgba(255,255,255,.5);
}
.trace__rows {
  margin: var(--space-4) 0 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: 1.7;
}
/* Label column sized to the longest label so the three values line up as a
   column — that alignment is what makes it read as output rather than prose. */
.trace__row { display: grid; grid-template-columns: 6.5rem 1fr; gap: var(--space-3); }
.trace__row + .trace__row { margin-top: var(--space-2); }
/* .45 would be the natural step down from the value, but it computes to 4.48:1
   on ink — a hair under AA for 14px text. .5 clears it at 5.2:1 and still reads
   as clearly secondary next to the white values. */
.trace__row dt { color: rgba(255,255,255,.5); }
/* The requested path is the one value that varies, so it carries the weight.
   overflow-wrap because a long URL must not push the card wider than its column. */
.trace__row dd { color: #fff; overflow-wrap: anywhere; }
.trace__note {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-dark);
  font-size: var(--text-sm);
  line-height: 1.6;
  color: rgba(255,255,255,.72);
}

/* Stacks below the two-column threshold the About and Founder grids use. */
@media (max-width: 860px) {
  .notfound { grid-template-columns: 1fr; align-items: start; }
  .notfound__copy { max-width: none; }
}

/* The email address on the two pages that have no footer to carry it: the 404
   and the contact.php result page. pdpa-notice.html §9 says the address is
   published on every page, and the contact.php error strings tell the visitor to
   email instead, so the address has to be on the page those strings land on. */
.notfound__foot,
.formresult__foot { margin-top: var(--space-4); font-size: var(--text-sm); color: var(--color-text-muted); }
.notfound__foot a,
.formresult__foot a { text-decoration: underline; text-underline-offset: 3px; overflow-wrap: anywhere; }

/* Utility */
[hidden] { display: none !important; }
