/* FLOA — one stylesheet, assembled from every component.
   Generated by build.mjs — edit the files under src/, not this one. */

/* ======================================================================
   design/tokens.css
   ====================================================================== */
/* The design system. Every component below draws from these and nothing else —
   no component may invent a colour, a radius or a spacing step of its own.

   FLOA — "Clear Water": Swiss-precise, light and airy. One teal accent, large
   type, generous air. RTL, mobile-first. */
:root {
  /* --- colour: four hues, each with a job ---------------------------------
     Paper is a cool tinted ground, cards are pure white. Previously both were
     effectively the same white (#FBFCFC vs #FFFFFF), so a card had nothing to
     sit ON and only its hairline separated it from the page. Making white the
     figure and the tint the ground is what gives the cards their lift. */
  --paper:        #E9F0EE;
  --surface:      #FFFFFF;
  --surface-sunk: #F2F7F5;   /* recessed panels INSIDE a white card */

  --ink:          #0F2A33;
  --ink-2:        #24444F;
  /* darkened to clear AA (4.5:1) against the tinted paper, not just against a
     white card — the section leads sit on paper, where #66797F fell to 3.94 */
  --muted:        #586C73;
  --muted-2:      #63757B;

  /* Colour MEANS something here; it is never decoration. A section is tinted by
     what it is saying, so the same hue always says the same thing:

       teal   FLOA itself — the offer, the advantage, the ask
       water  how it works — the ways in, the process, the questions
       clay   what is broken and what it costs — the pains, the urgency, "before"
       amber  the payoff — the proof, the work, the clients

     Teal is also the only ACTION colour. Every button, link, focus ring and
     selection is teal and nothing else, so the three section hues can appear
     anywhere without ever competing with the thing we want clicked.

     Each hue is a trio and a component may only use the trio:
       base   the 2px hairline, the rule, the solid mark
       -dark  ANY text or icon in that hue (all four clear AA 4.5:1 on white,
              on paper, on sunk panels and on their own tint — checked, not
              guessed; the light `water` itself can never carry text)
       -tint  the soft fill behind -dark text (chips, badges, panels). Each one
              sits ≥1.14 from the paper, or the fill would simply vanish. */
  --teal:         #0E8C7E;
  --teal-dark:    #0A6C61;
  --teal-tint:    #C9E6DE;

  /* water carries a fourth weight the others do not need: --water-soft is the
     pale blue the hero illustrations were drawn with. The base had to leave it
     behind — at a luminance of .50 against teal's .20 and clay's .19, a 2px
     hairline in it simply read as grey. */
  --water:        #358FBC;
  --water-soft:   #7FC5E4;
  --water-dark:   #1C6483;
  --water-tint:   #C7E4F2;

  --clay:         #C1594A;
  --clay-dark:    #8E3527;
  --clay-tint:    #F0D7D0;

  --amber:        #D69A2D;
  --amber-dark:   #845611;
  --amber-tint:   #F2DDB2;

  --hairline:     #DCE6E3;
  --hairline-2:   #E9F0EE;

  /* the form's error state. Deliberately NOT --clay: a pain we are describing
     and a field the visitor got wrong must not look like the same thing. */
  --danger:       #A23B3B;
  --danger-bg:    #FCECEC;
  --danger-line:  #F0CFCF;

  /* --- type --- */
  --font-display: "Rubik", system-ui, "Segoe UI", Arial, sans-serif;
  --font-body:    "Assistant", system-ui, "Segoe UI", Arial, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, "Courier New", monospace;

  --fs-hero:  clamp(2.9rem, 6.4vw, 5.5rem);
  --fs-h2:    clamp(2.2rem, 4.1vw, 3.4rem);
  --fs-h3:    1.45rem;
  --fs-lead:  clamp(1.25rem, 1.6vw, 1.5rem);
  --fs-body:  1.25rem;
  --fs-sm:    1.14rem;
  --fs-xs:    .95rem;

  /* --- space: one 4px-based scale, used everywhere --- */
  --s-1:  4px;
  --s-2:  8px;
  --s-3:  12px;
  --s-4:  16px;
  --s-5:  20px;
  --s-6:  24px;
  --s-7:  32px;
  --s-8:  40px;
  --s-9:  56px;
  --s-10: 72px;

  /* Every inset is fluid. Fixed paddings stack up on narrow screens (container
     + panel + card) and crush the content column until text wraps one word per
     line — so each one scales with the viewport instead. */
  --gutter:   clamp(14px, 4vw, 24px);     /* container side padding */
  --gap:      clamp(14px, 2vw, 24px);     /* the ONE gap between grid items */
  --pad-card: clamp(18px, 3vw, 28px);     /* the ONE card padding */
  --pad-panel: clamp(20px, 5vw, 56px);    /* the ONE panel padding */
  /* the gap BETWEEN two sections. Each one pays half of it as padding, so two
     neighbours together add up to exactly this. */
  --section:  80px;
  --head-gap: clamp(36px, 4.5vw, 64px);   /* section head -> content */

  /* --- shape --- */
  --r-sm:  12px;
  --r:     18px;
  --r-lg:  24px;
  --r-pill: 999px;

  --field-h: 56px;           /* every input, select and button agrees */

  /* --- depth: soft, water-like, never heavy --- */
  --sh-1: 0 2px 6px rgba(15, 42, 51, .05);
  --sh-2: 0 10px 28px -12px rgba(15, 42, 51, .16);
  --sh-3: 0 26px 64px -28px rgba(15, 42, 51, .24);
  --sh-teal: 0 12px 28px -12px rgba(14, 140, 126, .45);

  --container: 1120px;
  --ease: cubic-bezier(.22, .61, .36, 1);
}

/* On a phone the scale itself tightens — every component inherits the change,
   so none of them needs a breakpoint of its own for spacing. */
@media (max-width: 640px) {
  :root {
    --gap: 16px;
    --pad-card: 22px;
    --fs-body: 1.16rem;
    --section: 56px;
    --head-gap: clamp(30px, 4.5vw, 64px);
  }
}

/* ======================================================================
   design/base.css
   ====================================================================== */
/* Reset & base. */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* -webkit-tap-highlight-color: a phone washes anything it reads as a link in
   grey-blue while a finger is down on it, over the whole shape and after the
   press. Buttons and cards here already say they were pressed. It inherits, so
   the page is done with it in one line. */
html {
  scroll-behavior: smooth;
  scroll-padding-top: 0;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.7;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg { max-width: 100%; display: block; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; padding: 0; }
button { font-family: inherit; cursor: pointer; }

:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 3px;
  border-radius: var(--s-1);
}

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

/* ======================================================================
   design/layout.css
   ====================================================================== */
/* Layout primitives — the only rules that are not owned by a component. */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* An anchor jump aligns the section's border box to the top of the viewport —
   which then leaves the section's whole top padding (up to 128px) as dead space
   above the heading. Pulling the scroll target up by that padding lands the
   CONTENT a normal ~40px below the fold instead. */
.section {
  padding-block: calc(var(--section) / 2);
  scroll-margin-top: calc(var(--s-8) - var(--section) / 2);
  position: relative;
}

main, .site-footer { position: relative; z-index: 1; }

/* The proof band is the one section that stacks several unrelated blocks — a
   flow, a project, a screenshot, a quote — so its parts need air between them.
   The head's own margin collapses with this, so it keeps its larger gap. */
.proof > .container > * + * { margin-top: var(--s-8); }

/* Long unbroken strings (emails, URLs) must never widen a track — but that is
   only true of body text. Headings are excluded on purpose: break-word would
   snap a Hebrew word in half mid-word on a narrow screen. They wrap between
   words only, and balance so the lines come out even. */
p, li, label, a, strong, span { overflow-wrap: break-word; }
h1, h2, h3, h4 { overflow-wrap: break-word; text-wrap: balance; }
p { text-wrap: pretty; }

/* ======================================================================
   design/typography.css
   ====================================================================== */
/* Typography. Component type sizes live with their component; this is the base. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.18;
  letter-spacing: -.015em;
  color: var(--ink);
}

/* Mono has no Hebrew glyphs — it is used ONLY for numerals and Latin, never
   for a Hebrew label, which would silently fall back to a system font. */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

.emphasis { color: var(--teal-dark); font-weight: 600; }

/* ======================================================================
   components/button/button.css
   ====================================================================== */
/* Buttons keep their label on a single line (white-space:nowrap). The labels are
   deliberately short, and on a phone the CTA row stacks so each button takes the
   full width (see hero-actions) — so a nowrap label has the whole row to sit on
   and never forces the page wider than the viewport. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-width: 0;
  max-width: 100%;
  white-space: nowrap;
  min-height: var(--field-h);
  padding: var(--s-3) var(--s-7);
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  font-family: var(--font-body);
  font-size: 1.12rem;
  font-weight: 700;
  line-height: 1.3;
  text-align: center;
  transition: transform .2s var(--ease), box-shadow .25s var(--ease),
              background-color .2s var(--ease), color .2s var(--ease),
              border-color .2s var(--ease);
}
.btn:active { transform: translateY(1px); }

/* --- size & fit ----------------------------------------------------------- */
.btn-lg { min-height: 62px; padding-inline: var(--s-8); font-size: 1.18rem; }
/* On a phone a large button spans the whole row (hero-actions stacks, the
   WhatsApp CTA fills the panel). With the label kept on one line, trim the side
   padding so the longest CTA still fits the row instead of spilling past it. */
@media (max-width: 640px) {
  .btn-lg { padding-inline: var(--s-5); }
}
.btn-block { width: 100%; }
/* opts a full-width button (the WhatsApp one) back down to its own content, so
   it can sit as a centred pill outside a form panel */
.btn-inline { display: inline-flex; width: auto; max-width: 100%; }

/* --- variants ------------------------------------------------------------- */
.btn-primary {
  background: var(--teal);
  color: #fff;
  box-shadow: var(--sh-teal);
}
.btn-primary:hover {
  background: var(--teal-dark);
  transform: translateY(-2px);
  box-shadow: 0 18px 36px -14px rgba(14, 140, 126, .55);
}
.btn-primary:disabled { opacity: .6; cursor: not-allowed; transform: none; }

.btn-ghost {
  background: var(--surface);
  color: var(--teal-dark);
  border-color: var(--hairline);
  box-shadow: var(--sh-1);
}
.btn-ghost:hover {
  border-color: rgba(14, 140, 126, .4);
  color: var(--teal);
  transform: translateY(-2px);
  box-shadow: var(--sh-2);
}

/* ======================================================================
   components/whatsapp-button/whatsapp-button.css
   ====================================================================== */
/* ==========================================================================
   Deliberately after button.css: .btn-whatsapp and .btn carry the same
   specificity, so what lands last wins.

   WhatsApp is the PRIMARY action on every page, so this is the loud button —
   but it is loud in FLOA's teal, not in WhatsApp's green.

   The reason: the MARK already says "WhatsApp". Once the glyph carries the
   channel, the colour is free to carry the brand, and the page keeps one accent
   instead of two greens fighting each other. A #25D366 button reads as a
   third-party widget dropped onto the page; this reads as the site's own
   primary action — which is exactly what it is.
   ========================================================================== */
.btn-whatsapp {
  gap: var(--s-3);
  background: var(--teal);
  color: #fff;
  border-color: var(--teal);
  box-shadow: var(--sh-teal);
}
.btn-whatsapp:hover {
  background: var(--teal-dark);
  border-color: var(--teal-dark);
  color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 18px 36px -14px rgba(14, 140, 126, .55);
}

/* --- the quiet skin --------------------------------------------------------
   Inside the contact panel, WhatsApp is NOT the main action: the form is, and
   WhatsApp sits under it as the alternative offered under an "or". Two solid
   teal pills stacked would be two primaries and no hierarchy, so there it wears
   .btn-ghost instead (see whatsapp-button.js). Nothing below cares which skin it
   is — the mark, the label and the one-line rule are bound to .btn-wa, which
   BOTH skins carry. */

/* the mark, always before the label (RTL: to its right, as the first child) */
.btn-wa .wa-mark {
  flex: 0 0 auto;
  display: inline-flex;
}
.btn-wa .wa-mark svg { width: 22px; height: 22px; }

/* THE ONE-LINE RULE. Every button on the site keeps its label on a single line
   (.btn sets white-space:nowrap), and these labels are the longest on the site
   — "לשיחת מיפוי ב־WhatsApp" on a full-width pill inside a 320px phone. So on a
   phone the mark, the gap and the side padding all give ground before the text
   does, and the label itself steps down a notch rather than wrapping. */
.btn-wa .wa-label { min-width: 0; }

@media (max-width: 640px) {
  .btn-wa {
    gap: var(--s-2);
    padding-inline: var(--s-4);
    font-size: 1.05rem;
  }
  .btn-wa .wa-mark svg { width: 20px; height: 20px; }
}
@media (max-width: 360px) {
  .btn-wa { padding-inline: var(--s-3); font-size: .98rem; }
}

/* ======================================================================
   components/chips/chips.css
   ====================================================================== */
/* the eyebrow's pill language, in a neutral tone */
.chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-2);
  margin-top: var(--s-7);
}
.chips li {
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--hairline);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: 1;
  box-shadow: var(--sh-1);
}

/* ======================================================================
   components/urgency/urgency.css
   ====================================================================== */
/* One line in a tinted panel. Clear but never aggressive: the colour is spent on
   a single edge and the faintest wash, the ink stays calm, and there is no
   warning mark and no motion beyond the shared reveal. It is not an error and
   must never look like one (see --danger in tokens.css).

   The hue is not decoration — it says who is speaking. The band defaults to
   clay, because it usually names the cost of waiting and so belongs to the pains
   it follows. Every rule below reads it from --accent, so a variant only ever
   re-points these two. */
.urgency {
  --accent: var(--clay);
  --accent-tint: var(--clay-tint);
  padding-block: var(--s-6);
}

/* teal: FLOA speaking in its own voice — the years behind the work, said once,
   right before the ask. */
.urgency-teal {
  --accent: var(--teal);
  --accent-tint: var(--teal-tint);
}

.urgency-line {
  max-width: 720px;
  margin-inline: auto;
  padding: var(--s-5) var(--pad-panel);
  background: color-mix(in srgb, var(--accent-tint) 40%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  border-inline-start: 4px solid var(--accent);
  border-radius: var(--r);
  text-align: center;
  color: var(--ink-2);
  font-family: var(--font-display);
  font-size: var(--fs-lead);
  font-weight: 500;
  line-height: 1.4;
  text-wrap: pretty;
}

/* ======================================================================
   components/cta-dock/cta-dock.css
   ====================================================================== */
/* The sticky mobile CTA. Hidden on desktop; on a phone it rests just off the
   bottom edge and slides up when active. It respects the home-indicator inset
   and keeps a ≥48px tap target. */
.cta-dock {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 60;
  padding-block: var(--s-2);
  padding-bottom: calc(var(--s-2) + env(safe-area-inset-bottom, 0px));
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--hairline);
  box-shadow: 0 -8px 24px -16px rgba(15, 42, 51, .3);
  transform: translateY(120%);
  transition: transform .28s var(--ease);
  will-change: transform;
}
.cta-dock.is-visible { transform: translateY(0); }
.cta-dock .btn {
  min-height: 52px;               /* comfortably past the 48px minimum */
  font-size: 1.12rem;
}

/* Desktop never sees it. */
@media (min-width: 769px) {
  .cta-dock { display: none; }
}

/* While the dock is up, reserve room at the foot of the page so it can never
   sit on top of real content. The dock hides again at the form, so this only
   applies mid-scroll and is toggled off-screen — no visible jump. */
@media (max-width: 768px) {
  body.cta-dock-active { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); }
}

@media (prefers-reduced-motion: reduce) {
  .cta-dock { transition: none; }
}

/* ======================================================================
   components/section-head/section-head.css
   ====================================================================== */
.section-head {
  max-width: 46rem;
  margin-inline: auto;
  margin-bottom: var(--head-gap);
  text-align: center;
}
.section-head h2 { font-size: var(--fs-h2); }
.section-head p {
  margin-top: var(--s-4);
  margin-inline: auto;
  max-width: 40rem;
  font-size: var(--fs-lead);
  color: var(--muted);
}

/* eyebrow: a chip. Sets the section's topic before the headline lands.
   inline-flex, so it is centred by the head's text-align like any inline box. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  margin-bottom: var(--s-5);
  padding: var(--s-2) var(--s-4);
  border-radius: var(--r-pill);
  background: var(--teal-tint);
  color: var(--teal-dark);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  line-height: 1;
}

/* --- the chip takes the colour of what its section is saying ---------------
   Scrolling the page, the chips read as a legend: clay when it hurts, water
   when we explain, amber when we prove it, teal when FLOA asks for the job.
   The tone comes from the section's OWN class or id — the ones it already
   carries — so no page has to author a colour, and a section can never be
   tinted against its meaning. Anything not named here stays teal, the default
   and the safe one. */
.problem .eyebrow {
  background: var(--clay-tint);
  color: var(--clay-dark);
}
#outcome .eyebrow,                 /* what comes out the other end */
#tracks .eyebrow,                  /* the ways in */
.process .eyebrow,
.faq-section .eyebrow {            /* the questions */
  background: var(--water-tint);
  color: var(--water-dark);
}
#proof .eyebrow,                   /* home proves itself in a .solutions band, */
.proof .eyebrow,                   /* a solution page in a .projects one */
.projects .eyebrow,
.testimonial .eyebrow {
  background: var(--amber-tint);
  color: var(--amber-dark);
}

/* ======================================================================
   components/card/card.css
   ====================================================================== */
/* ==========================================================================
   The one card. Every boxed item on the site is this box — declared once here
   instead of once per section. The variants below only ever change the accent,
   the badge and the type scale; the surface, hairline, radius, padding, shadow
   and hover are shared and cannot drift apart.
   ========================================================================== */
.card {
  /* The card's hue, in one place. A variant below re-points these three and
     every rule that follows — the top hairline, the icon badge, the hover edge —
     comes along with it. A card never names a colour twice. */
  --accent:      var(--teal);
  --accent-dark: var(--teal-dark);
  --accent-tint: var(--teal-tint);

  height: 100%;                     /* every card in a row ends at the same line */
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: var(--pad-card);
  box-shadow: var(--sh-1);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease),
              border-color .25s var(--ease);
}
.card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
  box-shadow: var(--sh-2);
}
.card h3 { font-size: var(--fs-h3); margin-bottom: var(--s-2); }
.card p  { color: var(--muted); font-size: var(--fs-sm); }

/* --- accents: a hairline of colour along the top --------------------------
   Which hue a card wears is not a style choice — it is what the card is SAYING.
   See the map in tokens.css: water = how it works, clay = what it costs,
   amber = the proof, teal = FLOA. The badge follows the hairline automatically. */
.card-water { --accent: var(--water); --accent-dark: var(--water-dark); --accent-tint: var(--water-tint); }
.card-clay  { --accent: var(--clay);  --accent-dark: var(--clay-dark);  --accent-tint: var(--clay-tint); }
.card-amber { --accent: var(--amber); --accent-dark: var(--amber-dark); --accent-tint: var(--amber-tint); }

.card-teal,
.card-water,
.card-clay,
.card-amber { border-top: 2px solid var(--accent); }

/* The hover above softens `border-color`, which is all four sides — and that
   swallowed the accent hairline, the one thing on the card that says what it
   MEANS. The hue must not fade just because the pointer is over it, so it is
   restored here, after the hover, at full strength. */
.card-teal:hover,
.card-water:hover,
.card-clay:hover,
.card-amber:hover { border-top-color: var(--accent); }

/* --- size: tighter type for the dense grids (pains, ecosystem tiles) ------- */
.card-sm h3 { font-size: 1.08rem; margin-bottom: var(--s-1); }
.card-sm p  { font-size: var(--fs-xs); }

/* --- the badge the icon sits in ------------------------------------------- */
.card-icon {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  color: var(--accent-dark);      /* -dark, never the base: the base carries no text */
  background: var(--accent-tint);
}
.card-icon-tile {
  width: 48px;
  height: 48px;
  margin-bottom: var(--s-5);
  border-radius: var(--r-sm);
}
.card-icon-tile svg { width: 24px; height: 24px; }

/* the small circle the checklist uses — the tick beside a line of copy */
.card-icon-dot {
  width: 34px;
  height: 34px;
  margin-top: 2px;
  border-radius: 50%;
}
.card-icon-dot svg { width: 18px; height: 18px; }

/* --- row: the badge sits BESIDE the copy ---------------------------------- */
.card-row {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--s-4);
}
.card-row .card-icon-tile { margin-bottom: 0; }
.card-row .card-body { min-width: 0; }

/* the wide card (one solution takes the whole row) centres its badge instead */
.card-wide { align-items: center; gap: var(--s-6); }

/* --- "you are here": the current page's tile in the ecosystem grid ---------
   Always teal, whatever the grid around it: this tile is FLOA saying where you
   stand, not a section making a point. */
.card.is-current {
  border-color: color-mix(in srgb, var(--teal) 45%, transparent);
  background: var(--teal-tint);
  box-shadow: var(--sh-2);
}
.card.is-current:hover { transform: none; }
.card.is-current .card-icon { background: var(--teal); color: #fff; }

/* --- the arrow-link at the foot of a card that is itself a link -------------
   Not a button: pressing anywhere on the card already goes there. It is a
   signpost — it names the destination and moves when the card is hovered, so
   the card reads as clickable without a second, competing control inside it.
   `margin-top: auto` pins it to the floor, so two cards of unequal copy still
   end on the same line. Same recipe as .project-link. */
.card-cta {
  margin-top: auto;
  padding-top: var(--s-6);
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--accent-dark);
  transition: color .18s var(--ease);
}
.card-cta::after { content: "←"; transition: transform .2s var(--ease); }
.card:hover .card-cta::after { transform: translateX(-4px); }

.card-badge {
  align-self: flex-start;
  margin-top: var(--s-3);
  display: inline-flex;
  align-items: center;
  padding: 3px var(--s-3);
  border-radius: var(--r-pill);
  background: var(--teal);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.4;
}

/* ==========================================================================
   The grid. `cards-N` is the DESKTOP column count — the two breakpoints below
   take every one of them down to 2 and then to 1, so a section never writes a
   media query of its own.
   ========================================================================== */
.card-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  min-width: 0;
}
/* a track must be allowed to shrink below its content's min-content width, or a
   long unbroken Hebrew string pushes a card out of its column */
.card-grid > * { min-width: 0; }

.cards-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.cards-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* one card may span the whole row, whatever the column count */
.card-grid .card-wide { grid-column: 1 / -1; }

/* the checklist and the ecosystem tiles: a narrower measure, centred */
.cards-narrow { max-width: 940px; margin-inline: auto; }

@media (max-width: 1080px) {
  .cards-3, .cards-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 860px) {
  .card-grid { grid-template-columns: 1fr; }
  /* stacked, the wide card has a full row to itself — so it stops being wide */
  .card-wide { flex-direction: column; align-items: flex-start; }
  .card-wide .card-icon-tile { margin-bottom: var(--s-5); }
}

/* ======================================================================
   components/eco-grid/eco-grid.css
   ====================================================================== */
/* The ecosystem row owns exactly two things the shared card does not: a row that
   fits five tiles at any width, and the line of copy that closes it. Everything
   else about these tiles — the box, the icon badge, the "you are here" state —
   is card.css, and is not repeated here. */
.eco-grid {
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}

.eco-note {
  margin-top: var(--gap);
  margin-inline: auto;
  max-width: 46rem;
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-sm);
}
.eco-note a { color: var(--teal-dark); font-weight: 700; }
.eco-note a:hover { text-decoration: underline; }

/* ======================================================================
   components/quote-card/quote-card.css
   ====================================================================== */
.quotes-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  max-width: 1000px;
  margin-inline: auto;
}
/* A client saying it worked is the strongest proof on the page, so the card
   wears the proof hue: amber on the mark and along the top edge. Same 2px
   hairline the cards use — a quote is evidence, and it is filed as such. */
.quote-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-top: 2px solid var(--amber);
  border-radius: var(--r-lg);
  padding: clamp(28px, 4vw, 48px);
  box-shadow: var(--sh-2);
  text-align: center;
}
.quote-mark {
  display: block;
  font-family: var(--font-display);
  font-size: 4.5rem;
  line-height: .5;
  height: .55em;
  color: var(--amber);
}
.quote-text {
  font-size: var(--fs-lead);
  line-height: 1.75;
  font-weight: 600;
  color: var(--ink-2);
}
.quote-by {
  margin-top: auto;
  padding-top: var(--s-7);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.quote-name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--ink);
}
.quote-role { color: var(--muted); font-size: var(--fs-sm); }

/* one quote on a page gets the full width to itself */
.quotes-grid:has(.quote-card:only-child) { max-width: 720px; grid-template-columns: 1fr; }

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

/* ======================================================================
   components/project-card/project-card.css
   ====================================================================== */
.projects-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  min-width: 0;
}
.projects-grid > * { min-width: 0; }
/* one project on a page gets the centre, not half the row */
.projects-grid:has(> :only-child) { grid-template-columns: 1fr; max-width: 720px; margin-inline: auto; }

.project-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: var(--sh-1);
  transition: transform .25s var(--ease), box-shadow .25s var(--ease),
              border-color .25s var(--ease);
}
.project-card:hover {
  transform: translateY(-4px);
  border-color: rgba(14, 140, 126, .28);
  box-shadow: var(--sh-2);
}

/* Square, because that is the shape of the captures themselves. `cover` crops
   whatever does not fit, and these are composed frames — the headline sits in one
   corner, the phone in another — so any mismatch here costs content, not margin. */
.project-media {
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  border-bottom: 1px solid var(--hairline);
  background: var(--surface-sunk);
}
.project-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform .5s var(--ease);
}
.project-card:hover .project-media img { transform: scale(1.03); }

.project-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: var(--pad-card);
}
.project-name {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .14em;
  color: var(--teal-dark);
}
.project-body h3 {
  font-size: 1.35rem;
  margin: var(--s-2) 0 var(--s-6);
  line-height: 1.25;
}
.project-body h3 a { transition: color .18s var(--ease); }
.project-body h3 a:hover { color: var(--teal-dark); }

/* the parts share one rhythm and a hairline ladder */
.project-part {
  padding-block: var(--s-4);
  border-top: 1px solid var(--hairline-2);
}
.project-label {
  display: block;
  margin-bottom: var(--s-1);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--muted-2);
}
.project-part p { color: var(--muted); font-size: var(--fs-sm); }

.project-link {
  margin-top: auto;
  padding-top: var(--s-6);
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  font-weight: 700;
  color: var(--teal-dark);
  transition: color .18s var(--ease);
}
.project-link::after { content: "←"; transition: transform .2s var(--ease); }
.project-link:hover { color: var(--teal); }
.project-link:hover::after { transform: translateX(-4px); }

/* --- two shots sharing one media area — side by side, stacked on mobile ---- */
.project-media-duo {
  aspect-ratio: auto;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap);
  padding: var(--pad-card);
  background: var(--surface-sunk);
}
.project-media-duo .duo-item {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* only when there are exactly two shots does the first one take more room —
   with three, they share the strip evenly */
.project-media-duo .duo-item:first-child:nth-last-child(2) { flex-grow: 1.45; }

/* a held slot inside the media strip sits on white, so its dashed edge reads
   against the sunk background around it */
.project-media-duo .placeholder-box { background: var(--surface); }
.project-media-duo img {
  max-width: 100%;
  max-height: clamp(230px, 26vw, 320px);
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--r-sm);
  box-shadow: var(--sh-2);
  transition: transform .5s var(--ease);
}
.project-card:hover .project-media-duo img { transform: none; }
.project-media-duo a.duo-item:hover img { transform: translateY(-3px); }

@media (max-width: 1080px) { .projects-grid { grid-template-columns: 1fr; } }
@media (max-width: 640px) {
  .project-media-duo { flex-direction: column; }
  .project-media-duo .duo-item { width: 100%; flex: none; }
  .project-media-duo img { width: 100%; height: auto; max-height: none; }
}

/* ======================================================================
   components/flow/flow.css
   ====================================================================== */
/* The arrow lives in a pseudo-element between the steps, so the markup stays a
   plain ordered list — and the direction can flip at a breakpoint without
   touching the HTML. */
.flow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--s-3);
  max-width: 940px;
  margin-inline: auto;
}
.flow-step {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  min-width: 0;
}
.flow-label {
  display: inline-flex;
  align-items: center;
  padding: var(--s-3) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--surface);
  border: 1px solid var(--hairline);
  box-shadow: var(--sh-1);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
}
/* the last step is the outcome — it lands in the accent */
.flow-step:last-child .flow-label {
  background: var(--teal-tint);
  border-color: rgba(14, 140, 126, .45);
  color: var(--teal-dark);
  font-weight: 700;
}
/* RTL: the row runs right to left, so the arrow points left */
.flow-step:not(:last-child)::after {
  content: "←";
  color: var(--teal);
  font-weight: 700;
  line-height: 1;
}

/* stacked: inside a card, and on any phone */
.flow-vertical { flex-direction: column; align-items: stretch; }
.flow-vertical .flow-step { flex-direction: column; gap: var(--s-2); }
.flow-vertical .flow-label { width: 100%; justify-content: center; }
.flow-vertical .flow-step:not(:last-child)::after { content: "↓"; }

@media (max-width: 860px) {
  .flow { flex-direction: column; align-items: stretch; }
  .flow-step { flex-direction: column; gap: var(--s-2); }
  .flow-label { width: 100%; justify-content: center; }
  .flow-step:not(:last-child)::after { content: "↓"; align-self: center; }
}

/* --- several flows, side by side ------------------------------------------
   The box itself is .card (see card.js) — the surface, hairline, radius, padding
   and shadow are NOT re-declared here. All a flow card adds is a title above the
   steps and the air between them. */
.flow-cards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap);
  min-width: 0;
}
.flow-cards > * { min-width: 0; }

.flow-card { gap: var(--s-5); }
.flow-card h3 { margin-bottom: 0; text-align: center; font-size: 1.12rem; }
/* the steps take whatever height is left, so cards in a row stay level */
.flow-card .flow { flex: 1; justify-content: center; }

@media (max-width: 860px) { .flow-cards { grid-template-columns: 1fr; } }

/* ======================================================================
   components/timeline/timeline.css
   ====================================================================== */
.timeline {
  position: relative;
  max-width: 780px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}
/* The rail runs the same four hues as the numbers beside it, in the same order,
   so it reads as their legend rather than as decoration. */
.timeline::before {
  content: "";
  position: absolute;
  inset-inline-start: 27px;
  top: 28px;
  bottom: 28px;
  width: 1px;
  background: linear-gradient(180deg,
    var(--water) 0%, var(--amber) 34%, var(--clay) 67%, var(--teal) 100%);
  opacity: .45;
}
.tl-item {
  position: relative;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: var(--s-6);
  align-items: start;
  min-width: 0;
}
.tl-item > * { min-width: 0; }

/* Each step wears the hue of what it IS — the process walks the whole palette
   and lands on the brand: we map what hurts you (water is the cool, neutral
   look at it), we plan, we build, and at 04 the system goes live in FLOA's own
   teal. It is the one place on the site where all four hues appear together,
   and it earns it: it is the only sequence we ever show.
     01 map      water
     02 plan     amber
     03 build    clay
     04 live     teal
   A fifth step would simply start the cycle again — nth-child(4n+1) — so the
   process can grow without anyone re-authoring a colour. */
.tl-item:nth-child(4n+1) { --accent: var(--water); --accent-dark: var(--water-dark); }
.tl-item:nth-child(4n+2) { --accent: var(--amber); --accent-dark: var(--amber-dark); }
.tl-item:nth-child(4n+3) { --accent: var(--clay);  --accent-dark: var(--clay-dark); }
.tl-item:nth-child(4n)   { --accent: var(--teal);  --accent-dark: var(--teal-dark); }

.tl-num {
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--surface);
  border: 1px solid var(--hairline);
  box-shadow: var(--sh-1);
  color: var(--accent-dark);
  font-family: var(--font-mono);
  font-size: .95rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  transition: background-color .25s var(--ease), color .25s var(--ease),
              border-color .25s var(--ease);
}
/* fills with the -dark tone, not the base: white on a light `water` would be
   unreadable, and all four -dark tones clear AA against white. */
.tl-item:hover .tl-num {
  background: var(--accent-dark);
  border-color: var(--accent-dark);
  color: #fff;
}
.tl-body {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: var(--s-6) var(--pad-card);
  box-shadow: var(--sh-1);
}
.tl-body h3 { font-size: var(--fs-h3); margin-bottom: var(--s-1); }
.tl-body p { color: var(--muted); font-size: var(--fs-sm); }

/* A step's own line, set off by a rule in that step's hue (--accent comes from
   the nth-child map above, so it needs no colour of its own). Darker ink than
   the copy above it, because it is the founder speaking and not a caption. */
.tl-note {
  margin-top: var(--s-4);
  padding-top: var(--s-4);
  padding-inline-start: var(--s-4);
  border-top: 1px solid var(--hairline-2);
  border-inline-start: 2px solid var(--accent);
}
.tl-body .tl-note { color: var(--ink-2); }

@media (max-width: 640px) {
  .tl-item { grid-template-columns: 44px 1fr; gap: var(--s-4); }
  .tl-num { width: 44px; height: 44px; font-size: .85rem; }
  .timeline::before { inset-inline-start: 21px; }
}

/* ======================================================================
   components/faq/faq.css
   ====================================================================== */
/* The accordion. Cards, like everything else on the page — the question is the
   whole hit area, and the mark is the only thing that moves. */
.faq {
  max-width: 800px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

/* Questions are water: this section explains, it does not sell. The teal is
   spent on the answer's links and on nothing else here. */
.faq-item {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  box-shadow: var(--sh-1);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.faq-item[open] {
  border-color: color-mix(in srgb, var(--water-dark) 30%, transparent);
  box-shadow: var(--sh-2);
}

/* the question: a real summary, so it is focusable and toggles on Enter/Space */
.faq-q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  padding: var(--pad-card);
  cursor: pointer;
  list-style: none;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: 600;
  color: var(--ink);
}
.faq-q::-webkit-details-marker { display: none; }   /* Safari's own triangle */
.faq-q:hover { color: var(--water-dark); }
.faq-q:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
  border-radius: var(--r);
}

/* a + that becomes a − : two bars, one of which rotates away when open */
.faq-mark {
  position: relative;
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--water-tint);
}
.faq-mark::before,
.faq-mark::after {
  content: "";
  position: absolute;
  inset: 50% 5px auto 5px;
  height: 2px;
  border-radius: 2px;
  background: var(--water-dark);
  transform: translateY(-50%);
  transition: transform .25s var(--ease), opacity .25s var(--ease);
}
.faq-mark::after { transform: translateY(-50%) rotate(90deg); }
.faq-item[open] .faq-mark::after { transform: translateY(-50%) rotate(0deg); opacity: 0; }

.faq-a {
  padding: 0 var(--pad-card) var(--pad-card);
  color: var(--muted);
  font-size: var(--fs-sm);
}

@media (max-width: 640px) {
  .faq-q { font-size: 1.2rem; }
}

/* ======================================================================
   components/ba-strip/ba-strip.css
   ====================================================================== */
.ba-strip {
  margin-top: var(--gap);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: stretch;
  gap: var(--gap);
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r);
  padding: var(--pad-card);
  box-shadow: var(--sh-1);
}
.ba-strip > * { min-width: 0; }

/* The strip is the whole argument in one line: it starts in clay, where it
   hurts, and ends in teal, where we leave you. The before side is only just
   tinted — enough to feel warm and wrong beside the after, never enough to look
   like an error. */
.ba-side {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  padding: var(--s-5);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--clay-tint) 38%, var(--surface-sunk));
}
.ba-side-after { background: var(--teal-tint); }
.ba-side p { color: var(--muted); font-size: var(--fs-sm); }
.ba-side-after p { color: var(--teal-dark); font-weight: 600; }

.ba-tag {
  align-self: flex-start;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: .06em;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-pill);
}
.ba-tag-before { background: var(--clay-tint); color: var(--clay-dark); }
.ba-tag-after  { background: var(--teal); color: #fff; }

/* the arrow points AT the outcome, so it belongs to the outcome: teal */
.ba-arrow { display: grid; place-items: center; color: var(--teal); }
.ba-arrow svg { width: 40px; height: 24px; }

@media (max-width: 640px) {
  .ba-strip { grid-template-columns: 1fr; }
  .ba-arrow svg { transform: rotate(-90deg); width: 24px; height: 36px; }
}

/* ======================================================================
   components/approach/approach.css
   ====================================================================== */
/* A line of lead copy above the picture and the principles. Same measure and
   same type as the lead under any section head (.section-head p), because that
   is what it is — the section's head continuing to speak, not a new device. */
.approach-intro {
  max-width: 40rem;
  margin-inline: auto;
  margin-bottom: var(--head-gap);
  text-align: center;
  font-size: var(--fs-lead);
  color: var(--muted);
  text-wrap: pretty;
}

.approach-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
  min-width: 0;
}
.approach-grid > * { min-width: 0; }

.principles {
  display: grid;
  gap: var(--s-6);
  align-content: center;
}
.principle {
  padding-inline-start: var(--s-5);
  border-inline-start: 2px solid var(--teal-tint);
  transition: border-color .25s var(--ease);
}
.principle:hover { border-inline-start-color: var(--teal); }
.principle h3 { font-size: 1.05rem; font-weight: 600; margin-bottom: var(--s-1); }
.principle p { color: var(--muted); font-size: var(--fs-sm); }

.approach-media img {
  width: 100%;
  border-radius: var(--r-lg);
  border: 1px solid var(--hairline);
  background: var(--surface);
  box-shadow: var(--sh-3);
}

@media (max-width: 1080px) {
  .approach-grid { grid-template-columns: 1fr; }
  .approach-media { order: -1; max-width: 560px; margin-inline: auto; }
}

/* ======================================================================
   components/compare/compare.css
   ====================================================================== */
.compare-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap);
  max-width: 980px;
  margin-inline: auto;
}
.compare-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--pad-card);
  box-shadow: var(--sh-1);
}
.compare-card h3 { font-size: var(--fs-h3); margin-bottom: var(--s-5); }

.compare-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin-bottom: var(--s-7);
}
.compare-list li {
  display: flex;
  align-items: flex-start;
  gap: var(--s-3);
  color: var(--ink-2);
  font-size: var(--fs-sm);
}
.compare-check {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  margin-top: 2px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--teal-tint);
  color: var(--teal-dark);
}
.compare-check svg { width: 13px; height: 13px; }

.compare-card .btn { margin-top: auto; }

.compare-badge {
  align-self: flex-start;
  margin-bottom: var(--s-4);
  padding: 3px var(--s-3);
  border-radius: var(--r-pill);
  background: var(--teal);
  color: #fff;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  line-height: 1.4;
}

/* the richer track: its own accent, not a bigger box */
.compare-teal {
  border-color: color-mix(in srgb, var(--teal) 35%, transparent);
  box-shadow: var(--sh-2);
}
.compare-teal h3 { color: var(--teal-dark); }

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

/* ======================================================================
   components/about/about.css
   ====================================================================== */
.about-inner { max-width: 720px; margin-inline: auto; text-align: center; }

.about-avatar {
  width: 132px;
  height: 132px;
  margin: 0 auto var(--s-7);
}
.about-avatar img {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  object-fit: cover;
  object-position: 50% 18%;
  border: 3px solid var(--surface);
  background: var(--teal-tint);
  box-shadow: var(--sh-3);
}

.about-inner h2 { font-size: var(--fs-h2); margin-bottom: var(--s-5); }
.about-inner p {
  color: var(--muted);
  margin-inline: auto;
  max-width: 58ch;
  margin-bottom: var(--s-4);
}

.about-inner .emphasis {
  display: block;
  margin-top: var(--s-6);
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 600;
  color: var(--teal-dark);
}

/* ======================================================================
   components/cta-band/cta-band.css
   ====================================================================== */
.cta-band { padding-block: var(--section); }
.cta-inner {
  max-width: 780px;
  margin-inline: auto;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--pad-panel);
  box-shadow: var(--sh-2);
}
.cta-inner h2 { font-size: var(--fs-h2); }
.cta-inner p {
  margin: var(--s-4) auto var(--s-8);
  color: var(--muted);
  font-size: var(--fs-lead);
  max-width: 44rem;
}

/* ======================================================================
   components/contact/contact.css
   ====================================================================== */
/* Every control shares one height, one border, one radius, one focus ring. */
.contact-panel {
  max-width: 800px;
  margin-inline: auto;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: var(--pad-panel);
  box-shadow: var(--sh-3);
}
.contact-panel .section-head { margin-bottom: var(--s-8); }

/* Two fields, side by side on a desktop and stacked on a phone. minmax(0,1fr),
   not 1fr: a bare 1fr track refuses to shrink below its content's min-content
   width, which pushes the inputs out of the panel on narrow screens. */
.contact-form {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-5);
}
.field {
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  min-width: 0;
}
.field label {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-2);
}

/* The spam trap. Moved off-screen rather than display:none — a bot that filters
   for visibility still fills it, and a screen reader still skips it (the field
   sits inside an aria-hidden wrapper and is out of the tab order). */
.hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* the single source of truth for control chrome */
.field input {
  width: 100%;
  min-height: var(--field-h);
  padding: var(--s-3) var(--s-4);
  font-family: var(--font-body);
  font-size: 1.08rem;
  line-height: 1.5;
  color: var(--ink);
  background-color: var(--surface-sunk);
  border: 1px solid var(--hairline);
  border-radius: var(--r-sm);
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease),
              background-color .18s var(--ease);
  appearance: none;
  -webkit-appearance: none;
}
.field input::placeholder { color: var(--muted-2); }
.field input:hover { border-color: #D2DEDB; }
.field input:focus {
  outline: none;
  background-color: var(--surface);
  border-color: var(--teal);
  box-shadow: 0 0 0 4px rgba(14, 140, 126, .13);
}

/* invalid state is applied by JS ONLY after a submit attempt — never on load */
.field input.is-invalid {
  border-color: var(--danger);
  box-shadow: 0 0 0 4px rgba(162, 59, 59, .10);
}

/* the submit sits in its own row with real air around it */
.form-actions {
  grid-column: 1 / -1;
  margin-top: var(--s-3);
}

/* "ללא התחייבות" — the quiet line right under the button */
.contact-note {
  margin-top: var(--s-4);
  text-align: center;
  color: var(--muted);
  font-size: var(--fs-xs);
}

.form-success,
.form-error {
  margin-top: var(--s-6);
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  align-items: center;
  text-align: center;
  padding: var(--s-5);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}
.form-success {
  background: var(--teal-tint);
  border: 1px solid rgba(14, 140, 126, .2);
  color: var(--teal-dark);
}
.form-success strong { font-size: 1.05rem; font-weight: 700; }
.form-error {
  background: var(--danger-bg);
  border: 1px solid var(--danger-line);
  color: var(--danger);
  font-weight: 600;
}
/* [hidden] must always win over the flex display above */
.form-success[hidden], .form-error[hidden] { display: none; }

/* --- the faster door, under the form --------------------------------------
   WhatsApp closes the panel so the visitor who would rather not wait for a call
   back doesn't have to scroll up to find it. The rule through the label keeps it
   reading as the ALTERNATIVE to the form above, not as a second submit. */
.contact-wa { margin-top: var(--s-7); }

.contact-or {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin-bottom: var(--s-5);
  color: var(--muted);
  font-size: var(--fs-xs);
}
.contact-or::before,
.contact-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

@media (max-width: 640px) { .contact-form { grid-template-columns: 1fr; } }

/* ======================================================================
   components/hero/hero.css
   ====================================================================== */
.hero {
  position: relative;
  padding-block: clamp(24px, 3vw, 40px) clamp(64px, 7vw, 96px);
  overflow: hidden;
}
.hero-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* letter-spacing appends a trailing gap AFTER the final letter, which makes the
   span's box wider than its glyphs — so a centred wordmark visibly sits off to
   one side. Force an LTR box and pull that trailing gap back off the end. */
.hero-logo {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 1.9rem);
  font-weight: 700;
  letter-spacing: .34em;
  direction: ltr;
  margin-inline-end: -.34em;
  color: var(--teal);
  margin-bottom: var(--s-6);
}
/* on every page but home the wordmark is the way back */
a.hero-logo { transition: color .18s var(--ease); }
a.hero-logo:hover { color: var(--teal-dark); }

.hero-title {
  font-size: var(--fs-hero);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -.03em;
  max-width: 16ch;
  text-wrap: balance;
}

.hero-text {
  margin-top: var(--s-6);
  font-size: var(--fs-lead);
  color: var(--muted);
  max-width: 46ch;
  text-wrap: pretty;
}

/* names the solution this page is about */
.hero-chip { margin-bottom: var(--s-6); }
.hero-chip span {
  display: inline-flex;
  align-items: center;
  padding: var(--s-2) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--teal-tint);
  color: var(--teal-dark);
  font-size: var(--fs-sm);
  font-weight: 700;
  line-height: 1.4;
}

.hero-actions {
  margin-top: var(--s-6);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-4);
}

/* the risk-lowering line under the buttons: small and gentle, but readable */
.hero-note {
  margin-top: var(--s-4);
  font-size: var(--fs-xs);
  color: var(--muted-2);
  max-width: 40ch;
  text-wrap: pretty;
}

@media (max-width: 860px) {
  .hero-actions { width: 100%; flex-direction: column; }
  .hero-actions .btn { width: 100%; }
}
@media (max-width: 640px) {
  .hero-title { max-width: none; }
}

/* ======================================================================
   components/hero-system/hero-system.css
   ====================================================================== */
.hero-system {
  position: relative;
  width: 100%;
  max-width: 780px;
  margin: clamp(28px, 3.5vw, 44px) auto clamp(8px, 1.5vw, 16px);
}
.system-svg { width: 100%; height: auto; overflow: visible; }

/* the cards */
.system-svg .plate { fill: var(--surface); stroke: var(--hairline); stroke-width: 1; }
.system-svg .bar   { fill: #E7EDEC; }
.system-svg .bar-teal { fill: rgba(14, 140, 126, .28); }
.system-svg .bar-blue { fill: rgba(127, 197, 228, .45); }
.system-svg .dot   { fill: #DCE5E3; }
.system-svg .dot-teal { fill: rgba(14, 140, 126, .55); }
.system-svg .dot-blue { fill: rgba(127, 197, 228, .75); }

/* a node at rest sits back; the lit one comes forward */
.system-svg .node {
  transform-box: fill-box;
  transform-origin: center;
  opacity: .62;
  transition: opacity .5s var(--ease), transform .5s var(--ease);
}
.system-svg .node.is-lit { opacity: 1; transform: scale(1.045); }
.system-svg .node.is-lit .plate { stroke: rgba(14, 140, 126, .45); }
.system-svg .node.is-lit .bar-teal { fill: var(--teal); }

/* the featured solution: pinned forward and lit while the rest still pulse.
   Placed AFTER .is-lit so its larger scale wins when both apply. */
.system-svg .node.is-feature { opacity: 1; transform: scale(1.07); }
.system-svg .node.is-feature .plate { stroke: var(--teal); stroke-width: 2; }
.system-svg .node.is-feature .bar-teal { fill: var(--teal); }

/* wires: drawn faint, and the lit one brightens */
.system-svg .wires path {
  opacity: .3;
  transition: opacity .5s var(--ease), stroke-width .5s var(--ease);
}
.system-svg .wires path.is-lit { opacity: .95; stroke-width: 2.6; }

/* the pulse riding each wire — positioned by JS along the path */
.system-svg .pulse { fill: var(--teal); opacity: 0; }
.system-svg .pulse.is-live { opacity: 1; }

/* the hub breathes, so the system reads as running even between steps */
@keyframes hub-breathe {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.035); }
}
.system-svg .hub {
  transform-box: fill-box;
  transform-origin: center;
  animation: hub-breathe 3.4s var(--ease) infinite;
}

/* the caption leads: it names the solution, then the diagram lights it */
.system-caption {
  margin-bottom: var(--s-5);
  text-align: center;
  min-height: 2.2em;
}
.system-caption-text {
  display: inline-flex;
  align-items: center;
  padding: var(--s-2) var(--s-5);
  border-radius: var(--r-pill);
  background: var(--teal-tint);
  color: var(--teal-dark);
  font-size: var(--fs-sm);
  font-weight: 700;
  line-height: 1.4;
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.system-caption.is-swapping .system-caption-text {
  opacity: 0;
  transform: translateY(6px);
}

/* ======================================================================
   components/hero-art/hero-art.css
   ====================================================================== */
.hero-art { width: 100%; }
.hero-illust {
  display: block;
  width: 100%;
  max-width: 700px;
  height: auto;
  margin: clamp(20px, 2.6vw, 36px) auto clamp(4px, 1vw, 12px);
  overflow: visible;
}

/* One shared palette for the whole illustration family. An .svg in
   src/content/hero-art/ never names a colour — it only picks from these. */
.hero-illust .plate      { fill: var(--surface); stroke: var(--hairline); stroke-width: 1; }
.hero-illust .plate-sunk { fill: var(--surface-sunk); stroke: var(--hairline); stroke-width: 1; }
.hero-illust .bar        { fill: #E7EDEC; }
.hero-illust .bar-teal   { fill: var(--teal); }
.hero-illust .bar-tint   { fill: rgba(14, 140, 126, .22); }
.hero-illust .bar-blue   { fill: rgba(127, 197, 228, .5); }
.hero-illust .dot        { fill: #D3E0DD; }
.hero-illust .fill-teal  { fill: var(--teal); }
.hero-illust .fill-tint  { fill: var(--teal-tint); }
.hero-illust .fill-white { fill: #fff; }
.hero-illust .fill-water { fill: rgba(127, 197, 228, .5); }
.hero-illust .wire       { fill: none; stroke: var(--water-soft); stroke-width: 2.2; stroke-linecap: round; opacity: .55; }
.hero-illust .wire-teal  { fill: none; stroke: var(--teal); stroke-width: 2.4; stroke-linecap: round; opacity: .85; }
.hero-illust .glyph      { fill: none; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.hero-illust .glyph-teal { stroke: var(--teal); }
.hero-illust .glyph-white{ stroke: #fff; }
.hero-illust .glyph-ink  { stroke: var(--ink-2); }
.hero-illust .glyph-muted{ stroke: #AFC0C0; }

.hero-illust .lift    { filter: drop-shadow(0 8px 12px rgba(15, 42, 51, .10)); }
.hero-illust .lift-lg { filter: drop-shadow(0 16px 24px rgba(15, 42, 51, .16)); }
.hero-illust .hub-glow{ filter: drop-shadow(0 10px 16px rgba(14, 140, 126, .40)); }

/* gentle life — frozen automatically under prefers-reduced-motion */
@keyframes hero-float   { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
@keyframes hero-breathe { 0%, 100% { transform: scale(1); }      50% { transform: scale(1.04); } }
@keyframes hero-blink   { 0%, 100% { opacity: .5; }              50% { opacity: 1; } }
.hero-illust .float   { animation: hero-float 5.5s var(--ease) infinite; transform-box: fill-box; transform-origin: center; }
.hero-illust .float-b { animation: hero-float 5.5s var(--ease) infinite; animation-delay: -2.75s; transform-box: fill-box; transform-origin: center; }
.hero-illust .breathe { animation: hero-breathe 3.8s var(--ease) infinite; transform-box: fill-box; transform-origin: center; }
.hero-illust .blink   { animation: hero-blink 2.6s var(--ease) infinite; }

/* ======================================================================
   components/footer/footer.css
   ====================================================================== */
.site-footer {
  margin-top: var(--section);
  background: var(--ink);
  color: #C3D3D7;
}
/* the whole footer sits on the page's centre axis, like every section head */
.footer-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-7);
  padding-block: var(--s-9);
  text-align: center;
}
.footer-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-1);
}
.logo-mark {
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: .3em;
  direction: ltr;
  margin-inline-end: -.3em;
  color: #fff;
}
.logo-sub { font-size: var(--fs-sm); color: #8FA4A9; }

.footer-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-3) var(--s-6);
}
.footer-links a {
  font-size: var(--fs-sm);
  color: #C3D3D7;
  transition: color .18s var(--ease);
}
.footer-links a:hover { color: #fff; }

.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, .09);
  padding-block: var(--s-5);
  font-size: var(--fs-xs);
  color: #8FA4A9;
  text-align: center;
}

/* ======================================================================
   components/legal/legal.css
   ====================================================================== */
.legal { padding-block: var(--s-8) var(--section); }
.legal .container { max-width: 760px; }

.legal-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
  flex-wrap: wrap;
  margin-bottom: var(--s-9);
  padding-bottom: var(--s-6);
  border-bottom: 1px solid var(--hairline);
}
.legal-brand {
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: .3em;
  direction: ltr;
  margin-inline-end: -.3em;
  color: var(--teal);
}
.legal-home {
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--teal-dark);
  transition: gap .2s var(--ease);
}
.legal-home:hover { text-decoration: underline; }

.legal h1 {
  font-size: clamp(2rem, 4.5vw, 3rem);
  margin-bottom: var(--s-2);
}
.legal-updated {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted-2);
  margin-bottom: var(--s-8);
}
.legal h2 {
  font-size: 1.3rem;
  margin: var(--s-8) 0 var(--s-4);
  padding-top: var(--s-6);
  border-top: 1px solid var(--hairline-2);
}
.legal p { color: var(--ink-2); margin-bottom: var(--s-4); }

.legal-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  margin-bottom: var(--s-5);
}
.legal-list li {
  position: relative;
  padding-inline-start: var(--s-6);
  color: var(--ink-2);
}
.legal-list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: .68em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--teal);
}

/* :not(.btn) — a bare `.legal a` rule (0,1,1) outranks `.btn-primary` (0,1,0)
   and would repaint the CTA's white label teal */
.legal a:not(.btn) { color: var(--teal-dark); font-weight: 600; }
.legal a:not(.btn):hover { text-decoration: underline; }

/* the closing CTA follows the site's centred language and is set off from the
   text above it, rather than stranded at the edge of the last paragraph */
.legal-foot {
  margin-top: var(--s-9);
  padding-top: var(--s-8);
  border-top: 1px solid var(--hairline);
  text-align: center;
}
.legal-foot p {
  color: var(--muted);
  font-size: var(--fs-sm);
  margin-bottom: var(--s-6);
}

/* ======================================================================
   components/resume/resume.css
   ====================================================================== */
/* ==========================================================================
   resume — a printed page that happens to be on the web.

   Everything is scoped under .cv. The body size is fixed at 10pt because a CV
   is read by people as well as parsers and 10pt is the floor for both; what
   flexes instead is --cv-gap, the air between sections. When the document has
   to land on a given number of sheets, that is the knob that turns.

   Millimetres, not pixels, because the target is A4. On screen 1mm resolves to
   ~3.78px, so the sheet is a true 210mm wide and what you see is what prints.

   No icons, no rules inside a section, no colour that carries meaning on its
   own. Everything on the paper is selectable text, in one column, in the order
   it should be read.
   ========================================================================== */
.cv {
  --cv-navy: #16356b;
  --cv-blue: #1d4e8f;
  --cv-slate: #4d6f9c;
  --cv-ink: #111;
  --cv-paper: #eceef2;
  --cv-rule: #ccd3dd;

  background: var(--cv-paper);
  /* the top padding is clearance, not taste: the controls are fixed to the top
     corner, and without it the sheet began underneath them */
  padding: 22mm 4mm 10mm;
  min-height: 100vh;
}

.cv-sheet {
  --cv-fs: 10pt;
  /* The air above and below each section rule, on PAPER. The screen sets its
     own, five times this. It came down from 2.4mm when Core Expertise gained a
     fourth group: squeezing that section alone recovered six of the eight
     millimetres needed to keep page one ending at Brillix, and this is the
     other two. */
  --cv-gap: 1.6mm;
  --cv-pad: 14mm;

  width: 210mm;
  max-width: 100%;
  margin-inline: auto;
  background: #fff;
  color: var(--cv-ink);
  padding: 13mm var(--cv-pad) 14mm;
  box-shadow: 0 2px 24px rgba(20, 40, 80, .14);

  font-family: "Inter", Arial, "Assistant", system-ui, sans-serif;
  font-size: var(--cv-fs);
  font-weight: 400;
  line-height: 1.2;

  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

[dir="rtl"] .cv-sheet { font-family: "Assistant", "Inter", Arial, system-ui, sans-serif; }

/* --- identity -------------------------------------------------------------- */
.cv-id__name {
  font-family: inherit;
  font-size: 25pt;
  font-weight: 800;
  line-height: 1.04;
  letter-spacing: -.005em;
  text-transform: uppercase;
  color: var(--cv-navy);
  margin-bottom: 2mm;
}

.cv-id__role {
  font-size: calc(var(--cv-fs) * 1.08);
  font-weight: 400;
  line-height: 1.34;
  color: var(--cv-blue);
}

/* One line, four items, separated by rules rather than by punctuation: a
   parser reads them in order and a person sees them at a glance. */
.cv-contact {
  list-style: none;
  padding: 0;
  margin-top: 3mm;
  display: flex;
  flex-wrap: wrap;
  gap: 1mm 4mm;
  font-size: calc(var(--cv-fs) * .96);
}

.cv-contact__item + .cv-contact__item {
  padding-inline-start: 4mm;
  border-inline-start: 1px solid var(--cv-rule);
}

.cv-contact__item a { color: inherit; }

/* --- a section ------------------------------------------------------------- */
.cv-sec {
  border-top: 1px solid var(--cv-rule);
  margin-top: var(--cv-gap);
  padding-top: var(--cv-gap);
}

.cv-sec__head {
  font-family: inherit;
  font-size: 13pt;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--cv-navy);
  margin-bottom: 1.8mm;
}

.cv-sec__body > p + p { margin-top: 1.4mm; }

/* --- bullets --------------------------------------------------------------- */
.cv-list {
  list-style: disc;
  padding-inline-start: 4.6mm;
  margin-top: 1.2mm;
}

.cv-list > li + li { margin-top: .6mm; }
.cv-list > li::marker { color: var(--cv-slate); }

/* --- one entry: the title on the line, its dates hard against the far edge -- */
.cv-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6mm;
}

.cv-row__dates {
  flex: none;
  color: var(--cv-slate);
  font-size: calc(var(--cv-fs) * .96);
}

.cv-job__role {
  font-family: inherit;
  font-size: var(--cv-fs);
  font-weight: 700;
  line-height: 1.28;
  letter-spacing: .01em;
  text-transform: uppercase;
  color: var(--cv-blue);
}

.cv-job__org {
  color: var(--cv-slate);
  font-weight: 600;
  margin-top: .3mm;
}

.cv-job + .cv-job { margin-top: 2.6mm; }

.cv-job__link { margin-top: 1.4mm; }

.cv-job__link a {
  color: var(--cv-blue);
  text-decoration: underline;
  text-underline-offset: .12em;
}

/* --- keyword groups: a run-in heading, then the terms after it ------------- */
.cv-kw + .cv-kw { margin-top: 1.4mm; }

.cv-kw__title {
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: var(--cv-navy);
}

/* --- education, military --------------------------------------------------- */
.cv-entry + .cv-entry { margin-top: 2mm; }

/* Set in the case the content is written in, never transformed: text-transform
   is all or nothing over a line, and over "B.Sc." it produces "B.SC.", which is
   not how the degree is written. */
.cv-entry__title {
  font-family: inherit;
  font-size: var(--cv-fs);
  font-weight: 700;
  line-height: 1.28;
  letter-spacing: .01em;
  color: var(--cv-blue);
}

/* --- the controls: screen only, never part of the document -----------------
   The download is a link to a PDF that already exists, not a window.print().
   Print puts the visitor back in the dialog whose header and footer are the
   reason the file is generated ahead of time in the first place. */
.cv-tools {
  position: fixed;
  inset-block-start: 6mm;
  inset-inline: 0;                  /* the full width, so the row can centre in it */
  z-index: 5;
  display: flex;
  justify-content: center;
  gap: 2mm;
  font-family: "Inter", Arial, system-ui, sans-serif;

  /* Spanning the viewport to centre would otherwise put an invisible bar across
     the top of the document that swallows every click landing on it. The bar
     ignores the pointer; the buttons take it back. */
  pointer-events: none;
}

.cv-tool {
  display: inline-flex;
  align-items: center;
  padding: 3mm 5.5mm;
  border-radius: 999px;
  background: #fff;
  color: var(--cv-navy);
  border: 1px solid var(--cv-rule);
  font-size: 11.5pt;
  pointer-events: auto;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 2px 10px rgba(20, 40, 80, .18);
}

.cv-tool--pdf {
  background: var(--cv-navy);
  color: #fff;
  border-color: var(--cv-navy);
}

.cv-tool:hover { filter: brightness(1.08); }

/* --- on a screen -----------------------------------------------------------
   Everything above this line is the PAPER setting: 10pt, leading of 1.2,
   section rules with almost no air, all of it fitted to a fold after Brillix.
   That setting exists to make two A4 pages and for no other reason, and read on
   a monitor at arm's length it is far too small.

   So the screen gets its own typography, in the same PROPORTIONS as the paper —
   the name is still two and a half times the body, the headings still a third
   above it — just larger, with the leading and the space between sections
   opened to match. The document still looks like the printed one; it is not
   pretending to be it at actual size.

   None of this reaches the PDF. `@media screen` does not apply when the media
   type is print, so the printed files are untouched by every rule below.
   ========================================================================== */
@media screen {
  .cv-sheet {
    --cv-fs: 16px;
    --cv-gap: 5mm;
    line-height: 1.5;
  }

  .cv-id__name { font-size: 40px; margin-bottom: 3mm; }
  .cv-id__role { line-height: 1.4; }
  /* Held below the body size, and tight, so the four items stay on ONE line.
     They are dividers, not bullets: wrapped, the second line opens with a rule
     that separates nothing from nothing. This is the one place on the page
     where the text does NOT grow with the rest. */
  .cv-contact { margin-top: 4mm; gap: 2mm 3mm; font-size: 14px; }
  .cv-contact__item + .cv-contact__item { padding-inline-start: 3mm; }

  .cv-sec__head { font-size: 22px; margin-bottom: 3mm; }
  .cv-sec__body > p + p { margin-top: 3mm; }

  .cv-list { margin-top: 2mm; }
  .cv-list > li + li { margin-top: 1.6mm; }

  .cv-job + .cv-job { margin-top: 6mm; }
  .cv-job__org { margin-top: 1mm; }
  .cv-job__link { margin-top: 2.5mm; }

  .cv-kw + .cv-kw { margin-top: 3mm; }
  .cv-entry + .cv-entry { margin-top: 3.5mm; }
}

/* --- narrower than a sheet -------------------------------------------------
   `screen and`, deliberately. Without it this fires when PRINTING too: the
   printed page box is 210mm minus the @page margins, which is narrower than a
   sheet by definition — so the phone layout would land on the paper. */
@media screen and (max-width: 218mm) {
  .cv { padding: 0; }
  .cv-sheet {
    --cv-pad: 6mm;
    width: 100%;
    padding: 9mm var(--cv-pad) 10mm;
    box-shadow: none;
  }
  .cv-contact { gap: 1mm 3mm; }
  .cv-contact__item + .cv-contact__item { padding-inline-start: 3mm; }
  .cv-row { flex-wrap: wrap; gap: 0 4mm; }
}

/* --- a phone ---------------------------------------------------------------
   The document above is set for PAPER: 10pt, leading of 1.2, section rules with
   almost no air, all of it fitted to a fold after Brillix. Held at arm's length
   on a phone that same setting is a wall.

   So on a phone it stops being a sheet and becomes a page to read: the text
   grows, the leading opens, and the space between sections comes back. None of
   it reaches the PDF — every rule here is inside `@media screen`, and a screen
   query does not apply when the media type is print, so the printed file is
   byte for byte what it was.

   The top padding is the second half of it. The controls are fixed to the top
   corner, and on a narrow screen the name ran underneath them; this is the
   clearance they need, and it exists only where they overlap. */
@media screen and (max-width: 700px) {
  .cv-sheet {
    --cv-pad: 5mm;
    padding: 26mm var(--cv-pad) 12mm;
  }

  /* the name is the one thing that has to come DOWN: at 40px it wrapped */
  .cv-id__name { font-size: 30px; }
  .cv-sec__head { font-size: 20px; }
  .cv-contact { gap: 2mm 3mm; }

  .cv-list { padding-inline-start: 5.5mm; }
  .cv-job__link a { overflow-wrap: anywhere; }

  /* a thumb needs more than a pointer does: 48px of height, which is the
     smallest target a phone should ever ask anyone to hit */
  .cv-tools { gap: 3mm; inset-block-start: 5mm; }
  .cv-tool {
    padding: 3.6mm 6mm;
    font-size: 17px;
    border-radius: 999px;
  }
}

/* --- print -----------------------------------------------------------------
   A4, and the sheet stops pretending to be a sheet: the paper IS the sheet, so
   the margins move to @page and the shadow goes. */
@media print {
  @page {
    size: A4;
    margin: 14mm;
  }

  /* The document stops where its text stops, and below that the page showed the
     SITE's background colour: a browser paints the body's background across the
     whole sheet, so the last page ended in a grey slab. Scoped with :has so
     printing any other page of the site is unaffected. */
  body:has(.cv) { background: #fff; }

  .cv {
    background: #fff;
    padding: 0;
    min-height: 0;
  }

  .cv-sheet {
    --cv-pad: 0mm;
    width: auto;
    max-width: none;
    margin: 0;
    padding: 0;
    box-shadow: none;
  }

  .cv-tools { display: none; }

  /* The one boundary the document declares, and it is the author's: the fold
     falls here and nowhere else. */
  .cv-job--page { break-before: page; }

  /* An employment entry is one thought and is never cut across a sheet. */
  .cv-job,
  .cv-entry,
  .cv-kw,
  .cv-sec__head,
  .cv-list > li { break-inside: avoid; }

  /* THE SQUEEZE, and it is confined to the keyword groups. Core Expertise
     carries a fourth group now, three lines of it, and page one has to end at
     Brillix — an employment entry is never cut, so anything that does not fit
     takes the whole entry with it and costs a third sheet. Nothing else is
     touched: not the summary, not a job, not the leading of the body.

     It reaches Technical Skills too, which shares the class and sits on page
     two with room to spare. Two keyword sections set differently would read as
     an accident rather than a decision. */
  .cv-kw { line-height: 1.1; }
  .cv-kw + .cv-kw { margin-top: .5mm; }

  /* a heading with nothing under it is a heading on the wrong page */
  .cv-sec__head { break-after: avoid; }
}

/* ======================================================================
   design/motion.css
   ====================================================================== */
/* Motion. Last in the bundle, so it can freeze anything any component started. */

/* Gated on .js: the reveal must only hide content once we know JS is running to
   bring it back. Hiding it unconditionally would leave the entire page below the
   hero invisible if the script ever fails to load. */
.js .reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .7s var(--ease), transform .7s var(--ease);
}
.js .reveal.in-view { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}
