
:root{
  --teal:#047D64;
  --mint:#50D6A6;
  --navy:#0B2340;
  --ink:#17304D;
  --muted:#607089;
  --line:rgba(11,35,64,.12);
  --soft:#F2F8F7;
  --white:#fff;
  --max:1240px;
  --serif:"Playfair Display",Georgia,serif;
  --sans:"DM Sans",system-ui,sans-serif;
  --shadow:0 20px 60px rgba(11,35,64,.11);
  /* Box corners. Circles (50%), pills (999px) and small details (2-5px)
     are deliberately not on this token — they are shapes, not corners. */
  --radius:10px;
  /* Section rhythm. Exposed as variables so they can be tuned live — open any
     page with ?tune to get a panel that writes these. */
  --hero-pad-top:54px;
  --hero-pad-bottom:30px;
  --eyebrow-gap:0px;
  --section-pad:40px;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{margin:0;color:var(--ink);background:var(--white);font-family:var(--sans);line-height:1.6;-webkit-font-smoothing:antialiased}
a{text-decoration:none;color:inherit}
/* height:auto matters now that the img tags carry width/height: without it a
   logo constrained to 140px wide would still be drawn at its intrinsic
   height. Rules that set an explicit height (cards, shots) still win. */
img{max-width:100%;height:auto;display:block}
button{font:inherit}
.container{width:min(calc(100% - 48px),var(--max));margin-inline:auto}
.eyebrow{margin:0 0 var(--eyebrow-gap);color:var(--teal);font-size:10px;font-weight:700;letter-spacing:.28em;text-transform:uppercase}
.serif{font-family:var(--serif)}
/* Width AND height aware: on a short window the 7vw size is what pushes
   the hero past the 100vh cap, and the cap clips rather than scrolls. The
   vh term only ever bites when the window is short. */
.display{font:400 clamp(52px,min(7vw,10.5vh),108px)/.92 var(--serif);letter-spacing:-.05em}
.display em,.section-title em{color:var(--teal);font-style:italic}
.section{padding:var(--section-pad) 0}
.section-title{margin:0;font:400 clamp(40px,4.6vw,68px)/.98 var(--serif);letter-spacing:-.035em}
.section-copy{max-width:610px;color:var(--muted);font-size:15px}
.text-link,.button-link{display:inline-flex;align-items:center;gap:10px;font-weight:700;color:var(--teal)}
.button-link{padding:14px 20px;border-radius:999px;background:var(--teal);color:#fff;transition:.25s ease}
.button-link:hover{transform:translateY(-2px);background:#06785f}
.button-link.alt{background:#fff;color:var(--navy);border:1px solid var(--line)}
.icon-btn{width:52px;height:52px;display:grid;place-items:center;border-radius:50%;background:var(--teal);color:#fff}
/* Floating WhatsApp button */
.wa-float{position:fixed;right:22px;bottom:22px;z-index:120;width:58px;height:58px;display:grid;place-items:center;
  border-radius:50%;background:#25D366;color:#fff;box-shadow:0 12px 28px rgba(11,35,64,.26);transition:.25s ease}
.wa-float:hover{transform:translateY(-3px) scale(1.04);box-shadow:0 16px 34px rgba(11,35,64,.32);color:#fff}
.wa-float:focus-visible{outline:3px solid var(--navy);outline-offset:3px}
.muted{color:var(--muted)}
/* Header */
.site-header{position:sticky;top:0;z-index:100;background:rgba(255,255,255,.92);backdrop-filter:blur(18px);border-bottom:1px solid rgba(11,35,64,.07)}
.nav{min-height:76px;display:flex;align-items:center;justify-content:space-between;gap:20px}
.brand{display:flex;align-items:center;flex:none}
.brand img{width:140px}
/* Items sit in their own padded hit areas rather than relying on a wide
   flex gap, so they stay comfortable as the row tightens. */
.nav-links{display:flex;align-items:center;gap:2px;font-size:12px;font-weight:600;color:#28405c}
.nav-links>a,.nav-links summary{display:flex;align-items:center;min-height:42px;padding:0 11px;border-radius:10px;white-space:nowrap;cursor:pointer;list-style:none;transition:color .2s ease,background .2s ease}
.nav-links>a:hover,.nav-links summary:hover{color:var(--teal);background:rgba(4,141,112,.07)}
.nav-links details{position:relative}
.nav-links summary::-webkit-details-marker{display:none}
.nav-caret{width:6px;height:6px;margin-left:8px;border-right:1.6px solid currentColor;border-bottom:1.6px solid currentColor;transform:translateY(-2px) rotate(45deg);transition:transform .25s ease}
.nav-links details[open] .nav-caret{transform:translateY(1px) rotate(-135deg)}
/* A closed <details> still lays out its absolutely-positioned children in
   some engines; hide it outright so it can never swallow a click. */
.nav-links details:not([open]) .dropdown{display:none}
.dropdown{position:absolute;top:calc(100% + 8px);left:0;min-width:232px;padding:10px;border:1px solid var(--line);border-radius:var(--radius);background:#fff;box-shadow:var(--shadow)}
.dropdown a{display:flex;align-items:center;min-height:42px;padding:0 12px;border-radius:10px}
.dropdown a:hover{background:var(--soft);color:var(--teal)}
.nav-toggle{display:none;width:44px;height:44px;margin-right:-10px;padding:0;align-items:center;justify-content:center;background:none;border:0;color:var(--navy);cursor:pointer}
.nav-toggle-box{position:relative;display:block;width:22px;height:15px}
.nav-toggle-box span{position:absolute;left:0;width:100%;height:2px;border-radius:2px;background:currentColor;transition:top .26s ease,transform .26s ease,opacity .18s ease}
.nav-toggle-box span:nth-child(1){top:0}
.nav-toggle-box span:nth-child(2){top:6.5px}
.nav-toggle-box span:nth-child(3){top:13px}
.nav-toggle[aria-expanded="true"] .nav-toggle-box span:nth-child(1){top:6.5px;transform:rotate(45deg)}
.nav-toggle[aria-expanded="true"] .nav-toggle-box span:nth-child(2){opacity:0}
.nav-toggle[aria-expanded="true"] .nav-toggle-box span:nth-child(3){top:6.5px;transform:rotate(-45deg)}
.nav-scrim{display:none}
.active{color:var(--teal)}
/* Hero */
/* Home hero is capped at the screen height. .hero already clips (the
   background bleeds past it), so the cap only works if the content is
   made to fit — hence the viewport-relative padding on .hero-inner and
   the height-aware heading below. Without those the cap would silently
   cut off the buttons on a short window. */
.hero{position:relative;overflow:hidden;min-height:min(780px,100vh);max-height:100vh;display:flex;align-items:center;background:#eaf4f3}
.hero-bg{position:absolute;inset:0;background:linear-gradient(90deg,rgba(255,255,255,1) 0%,rgba(255,255,255,.96) 25%,rgba(255,255,255,.58) 48%,rgba(255,255,255,0) 74%),var(--hero);background-size:auto,cover;background-position:center}
/* Blurred variant, for hero art that is detail-heavy enough to fight the
   headline (a screenshot, a busy interior) rather than sit behind it.
   The image and the white scrim are split onto two pseudo-elements so only
   the picture is softened: blurring the scrim as well would smear the very
   edge that is doing the work of making the text readable.
   ::before is inset PAST the element because blur samples transparent pixels
   outside the box and would otherwise leave a pale fringe down each side —
   .hero clips, so the overspill costs nothing. */
.hero-bg.is-blurred{background:none}
.hero-bg.is-blurred::before,.hero-bg.is-blurred::after{content:"";position:absolute}
.hero-bg.is-blurred::before{inset:calc(var(--hero-blur, 6px) * -4);
  background:var(--hero) center/cover;filter:blur(var(--hero-blur,6px))}
/* Never reaches transparent, unlike the sharp hero's scrim. That one sits
   under art chosen to be light where the words are; this variant has to hold
   navy text over whatever the picture happens to be, and a dark screenshot
   under a fully transparent edge measures about 0.7:1 — invisible. The .62
   floor keeps the worst case (text over a black pixel) at 5.6:1, and the
   heading's own band at .86+ is above 9:1. */
.hero-bg.is-blurred::after{inset:0;background:linear-gradient(90deg,rgba(255,255,255,.99) 0%,rgba(255,255,255,.96) 30%,rgba(255,255,255,.88) 52%,rgba(255,255,255,.62) 74%,rgba(255,255,255,.48) 100%)}
.hero-inner{position:relative;z-index:1;padding:clamp(24px,7vh,100px) 0 clamp(20px,6vh,86px)}
.hero-grid{display:grid;grid-template-columns:1.08fr .92fr;gap:70px;align-items:end}
.hero-copy{max-width:520px;margin:28px 0;color:#4f647d;font-size:16px}
.hero-actions{display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.scroll-mark{margin-top:48px;display:flex;align-items:center;gap:10px;color:#60707F;font-size:10px;letter-spacing:.2em;text-transform:uppercase}
.scroll-mark span{width:46px;height:1px;background:var(--teal)}
/* Interior hero */
/* Same cap as the home hero. On a desktop these run 500-560px, far under
   the ceiling, so it is a safety net rather than something that bites.
   It is released once the hero stacks (see the 820px block): in one column
   the copy sits above the visual and legitimately needs more than a screen,
   and unlike .hero this section does not clip — a binding cap here would
   spill the hero over the section below it rather than trim it. */
.interior-hero{padding:var(--hero-pad-top) 0 var(--hero-pad-bottom);max-height:100vh;background:linear-gradient(120deg,#f8fbfa,#e9f5f3)}
.interior-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:65px;align-items:center}
.interior-visual{min-height:460px;border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow);background-size:cover;background-position:center}
.interior-sub{margin-top:24px;color:var(--muted);font-size:16px;max-width:620px}
.pill-row{display:flex;gap:10px;flex-wrap:wrap;margin-top:28px}
.pill{padding:9px 13px;border:1px solid var(--line);border-radius:999px;color:#35516b;background:#fff;font-size:11px}
/* Services */
.services-head{display:grid;grid-template-columns:1fr .9fr auto;gap:40px;align-items:end;margin-bottom:58px}
/* Some section heads carry no third item (the home services block, the OTA
   core-management block). Without this the empty auto track still costs its
   40px gap, so the copy sits short of the edge for no reason. Degrades to
   that harmless gap wherever :has() is unsupported. */
.services-head:not(:has(> :nth-child(3))){grid-template-columns:1fr .9fr}
.services-grid{display:grid;grid-template-columns:repeat(4,1fr);border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
/* --py is the scroll parallax offset, written each frame by
   js/home-parallax.js. It goes through a custom property rather than
   transform directly for two reasons: an inline transform would beat the
   :hover lift below, and custom properties are not affected by the
   transition, so the drift tracks the scroll instead of easing 300ms
   behind it. The transition is therefore limited to background. */
.service-card{min-height:285px;padding:32px 26px;border-right:1px solid var(--line);transform:translate3d(0,var(--py,0px),0);transition:background .3s ease}
.service-card:last-child{border-right:0}
.service-card:hover{background:var(--soft);transform:translate3d(0,calc(var(--py,0px) - 4px),0)}
.service-icon{width:56px;height:56px;display:grid;place-items:center;border-radius:50%;margin-bottom:25px;background:#ecf9f5;color:var(--teal);font-size:23px}
.service-card h3{margin:0 0 9px;font-size:17px}
.service-card p{margin:0 0 22px;min-height:74px;color:var(--muted);font-size:13px}
/* Featured */
.alt-bg{background:var(--soft)}
.portfolio-head{display:flex;justify-content:space-between;gap:30px;align-items:end;margin-bottom:38px}
.project{display:grid;grid-template-columns:1.7fr .8fr;border-radius:var(--radius);overflow:hidden;background:#fff;border:1px solid var(--line)}
.project-visual{min-height:560px;position:relative;background-size:cover;background-position:center}
.browser{position:absolute;left:10%;right:10%;top:12%;bottom:9%;overflow:hidden;border-radius:var(--radius);background:#fff;box-shadow:0 30px 60px rgba(11,35,64,.28)}
.browser-bar{height:34px;display:flex;align-items:center;gap:6px;padding:0 12px;border-bottom:1px solid var(--line)}
.dot{width:7px;height:7px;border-radius:50%;background:#cfd9df}
.browser-screen{height:calc(100% - 34px);display:flex;align-items:center;padding:34px;background:linear-gradient(180deg,rgba(4,141,112,.08),transparent),var(--project);background-size:cover;background-position:center}
/* Holding a real homepage screenshot: no overlay, anchored to the top. */
.browser-screen.is-shot{padding:0;background:var(--project);background-size:cover;background-position:top center}
.browser-bar .case-url{flex:1;margin-left:8px;padding:4px 12px;border-radius:999px;background:var(--soft);color:var(--muted);font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.browser-card{max-width:470px;padding:28px;border-radius:var(--radius);background:rgba(255,255,255,.9);backdrop-filter:blur(8px)}
.browser-card h3{margin:8px 0;font:400 32px/1.06 var(--serif)}
.browser-card p{margin:0;color:var(--muted);font-size:12px}
.project-meta{display:flex;flex-direction:column;justify-content:center;padding:48px}
.project-meta h3{margin:10px 0 8px;font:400 40px/1 var(--serif)}
.project-meta p{margin:0;color:var(--muted)}
.project-actions{display:flex;gap:10px;margin-top:28px}
/* Covers <button> too: the slider swaps these anchors for real buttons, and
   a selector matching only `a` left them as default grey browser buttons. */
.project-actions a,.project-actions button{width:46px;height:46px;display:grid;place-items:center;padding:0;background:none;font:inherit;cursor:pointer;border:1px solid rgba(4,141,112,.3);border-radius:50%;color:var(--teal);transition:.25s}
.project-actions a:hover,.project-actions button:hover{background:var(--teal);color:#fff}
/* Why */
.why-grid{display:grid;grid-template-columns:1fr 1fr;gap:80px;align-items:center}
.benefits{border-left:1px solid var(--line);padding-left:48px}
.benefit{display:grid;grid-template-columns:56px 1fr;gap:18px;padding:23px 0;border-bottom:1px solid var(--line)}
.benefit:first-child{padding-top:0}
.benefit:last-child{border-bottom:0}
.benefit-icon{width:46px;height:46px;display:grid;place-items:center;border-radius:50%;background:#edf9f5;color:var(--teal)}
.benefit h3{margin:0 0 4px;font-size:16px}
.benefit p{margin:0;color:var(--muted);font-size:13px}
/* Logo row */
/* Service detail */
.detail-section{padding:var(--section-pad) 0}
.detail-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.detail-card{padding:30px;border:1px solid var(--line);border-radius:var(--radius);background:#fff}
.detail-card h3{margin:0 0 8px;font:400 30px/1.08 var(--serif)}
.detail-card p{margin:0;color:var(--muted);font-size:13px}
/* Cards that answer the cursor. .hover-lift already carries the site's
   lift and shadow; this adds the colour half, so the movement reads as a
   deliberate response rather than a wobble. Only the cards that opt in by
   carrying the class get it — .detail-card itself is shared with five
   other pages that are meant to sit still.
   The reduced-motion rule further down cuts every duration to almost
   nothing, so those visitors get the end state without the travel. */
.detail-card.hover-lift{transition:transform .3s ease,box-shadow .3s ease,border-color .3s ease}
.detail-card.hover-lift:hover{border-color:#cfe6dd}
.detail-card.hover-lift h3{transition:color .3s ease}
.detail-card.hover-lift:hover h3{color:var(--teal)}
.list-grid{display:grid;grid-template-columns:1fr 1fr;gap:80px}
.check-list{list-style:none;padding:0;margin:24px 0 0}
.check-list li{padding:14px 0;border-bottom:1px solid var(--line);font-size:14px}
.check-list li::before{content:"✓";display:inline-grid;place-items:center;width:22px;height:22px;margin-right:10px;border-radius:50%;background:#edf9f5;color:var(--teal);font-weight:700}
/* Highlight panel — the full scope of a build, set as one tinted block so it
   reads as a summary rather than another body list. */
.highlight{padding:48px;border-radius:var(--radius);background:var(--soft);border:1px solid var(--line)}
/* auto-fit rather than a fixed count: three columns on desktop, two on a
   tablet, one on a phone, with no breakpoint doing the deciding. */
.highlight-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:0 34px;margin:30px 0 0;padding:0;list-style:none}
.highlight-grid li{display:flex;align-items:flex-start;gap:11px;padding:13px 0;border-bottom:1px solid var(--line);font-size:14px;line-height:1.45}
.highlight-grid li::before{content:"✓";flex:none;display:grid;place-items:center;width:21px;height:21px;margin-top:1px;
  border-radius:50%;background:#fff;color:var(--teal);font-size:11px;font-weight:700}
.platforms{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
.platform{padding:24px;border:1px solid var(--line);border-radius:var(--radius);background:var(--soft);font-weight:700}
/* "Built On" sub-block folded into the highlight panel rather than its own
   section. The chips need a white fill here — .platform's default var(--soft)
   would be invisible against the .highlight panel's own soft background. */
.highlight-tools{margin-top:36px;padding-top:32px;border-top:1px solid var(--line)}
.highlight-tools .eyebrow{margin-bottom:16px}
.highlight-tools .platforms{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
.highlight-tools .platform{background:#fff}
/* Rate table */
.rate-table{margin-top:16px;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius)}
.rate-row{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;padding:13px 14px;border-bottom:1px solid var(--line);font-size:12px}
.rate-row:last-child{border-bottom:0}
.rate-row.head{font-weight:700;background:#f8fbfb}
/* Social content */
.social-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px}
.social-card{position:relative;overflow:hidden;border-radius:var(--radius);border:1px solid var(--line);background:#fff}
.social-card img{width:100%;height:290px;object-fit:cover}
.social-card .caption{padding:18px}
.social-card h3{margin:0 0 6px;font-size:15px}
.social-card p{margin:0;color:var(--muted);font-size:12px}
/* Photo cards that answer the cursor. .hover-lift supplies the lift and
   shadow; this adds the part that suits an image — the photograph itself
   pushes in slightly, which reads as depth rather than as the whole card
   sliding. .social-card already clips its overflow, so the zoom is
   contained by the card edge. Slower than the lift (.45s against .3s) so
   the picture keeps moving a moment after the card settles.
   Reduced motion cuts every duration further down this file, so those
   visitors get the end state with no travel. */
.social-card.hover-lift{transition:transform .3s ease,box-shadow .3s ease,border-color .3s ease}
.social-card.hover-lift img{transition:transform .45s ease}
.social-card.hover-lift:hover{border-color:#cfe6dd}
.social-card.hover-lift:hover img{transform:scale(1.05)}
.social-card.hover-lift h3{transition:color .3s ease}
.social-card.hover-lift:hover h3{color:var(--teal)}
/* Social video wall.
   Each tile is a 9:16 facade — a poster frame with a play control — and the
   platform's own iframe replaces it on click. Sizing the tile and the frame
   identically means the wall does not jump when one turns into the other. */
.reel-wall{margin-top:34px}
.reel-wall .section-copy{margin:6px 0 20px}
.reel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:18px}
.reel{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:var(--radius);background:#fff;overflow:hidden}
.reel-play{position:relative;display:block;width:100%;aspect-ratio:9/16;padding:0;border:0;background:var(--soft);cursor:pointer}
.reel-play img{width:100%;height:100%;object-fit:cover}
/* Darkened foot so the white play mark keeps its contrast over a bright frame. */
.reel-play::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(11,35,64,0) 58%,rgba(11,35,64,.5))}
.reel-icon{position:absolute;left:50%;top:50%;z-index:1;transform:translate(-50%,-50%);width:52px;height:52px;
  display:grid;place-items:center;border-radius:50%;background:rgba(255,255,255,.93);color:var(--teal);
  box-shadow:0 8px 22px rgba(11,35,64,.22);transition:transform .25s ease,background .25s ease}
.reel-play:hover .reel-icon{transform:translate(-50%,-50%) scale(1.09);background:#fff}
.reel-play:focus-visible{outline:3px solid var(--teal);outline-offset:-3px}
.reel-frame{display:block;width:100%;aspect-ratio:9/16;border:0}
.reel-video{display:block;width:100%;aspect-ratio:9/16;background:var(--navy);object-fit:cover}
.reel-meta{display:flex;flex-direction:column;gap:5px;padding:13px 14px}
.reel-account{font-size:12px;font-weight:700;color:var(--navy)}
.reel-caption{margin:0;color:var(--muted);font-size:12px;line-height:1.45;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.reel-link{font-size:11px;font-weight:700;color:var(--teal)}
/* Portfolio */
.portfolio-tabs{display:flex;gap:10px;flex-wrap:wrap;margin:30px 0 50px}

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

   A wash of out-of-focus colour behind an oversized wordmark. The colour
   is four radial gradients on one element with a single heavy blur, rather
   than four blurred elements: one filter pass instead of four, and the
   gradients bleed into each other before the blur is applied, which is
   what stops it looking like separate circles.

   The element is inset PAST the section on every side because blur samples
   transparent pixels beyond its box and would otherwise leave a pale halo
   down the edges. The section clips, so the overspill costs nothing.

   Colours sit in custom properties: swapping the palette is four lines.
   --------------------------------------------------------------------- */
.portfolio-hero{
  --ph-blur:76px;
  /* Brand mint and teal carry the wash; the warm and lilac accents give it
     the depth the reference has without turning the page pink. */
  --ph-a:rgba(80,214,166,.62);
  --ph-b:rgba(4,141,112,.30);
  --ph-c:rgba(255,164,138,.40);
  --ph-d:rgba(150,158,214,.32);
  position:relative;overflow:hidden;isolation:isolate;
  padding:calc(var(--hero-pad-top) + 26px) 0 var(--hero-pad-bottom);
  background:#fbfbfc}
.ph-canvas,.ph-veil{position:absolute;inset:-18%;pointer-events:none;filter:blur(var(--ph-blur))}
.ph-canvas{z-index:0;background:
  radial-gradient(38% 44% at 33% 44%, var(--ph-a) 0%, transparent 68%),
  radial-gradient(30% 36% at 61% 28%, var(--ph-b) 0%, transparent 70%),
  radial-gradient(34% 40% at 67% 64%, var(--ph-c) 0%, transparent 72%),
  radial-gradient(26% 32% at 24% 70%, var(--ph-d) 0%, transparent 70%)}
/* Faint enough to tint the wordmark without eating its contrast: the word
   measures better than 7:1 on the pale ground before this, and this takes
   only a little off. */
.ph-veil{z-index:2;opacity:.34;background:
  radial-gradient(30% 34% at 45% 46%, var(--ph-a) 0%, transparent 72%),
  radial-gradient(24% 26% at 63% 58%, var(--ph-c) 0%, transparent 74%)}
.ph-inner{position:relative;z-index:1;text-align:center}
/* text-indent balances the trailing letter-space so the word stays centred. */
.ph-word{margin:0;font:600 clamp(42px,10.5vw,146px)/1 var(--sans);
  letter-spacing:.2em;text-indent:.2em;text-transform:uppercase;color:var(--navy);opacity:.86}
.ph-lead{margin:16px auto 0;max-width:18ch;
  font:400 clamp(23px,3vw,40px)/1.06 var(--serif);letter-spacing:-.025em;color:var(--ink)}
.ph-lead em{color:var(--teal);font-style:italic}
.ph-sub{margin:14px auto 0;max-width:62ch;color:var(--muted);font-size:15px}
.portfolio-hero .portfolio-tabs{justify-content:center;margin-bottom:0}
@media(max-width:820px){
  .portfolio-hero{--ph-blur:52px}
  .ph-word{letter-spacing:.14em;text-indent:.14em}
}.portfolio-tab{padding:10px 15px;border:1px solid var(--line);border-radius:999px;background:#fff;color:#46617b;font-weight:600;font-size:12px;cursor:pointer;transition:.2s ease}
.portfolio-tab:hover{border-color:var(--teal);color:var(--teal)}
.portfolio-tab.active,.portfolio-tab.active:hover{background:var(--teal);color:#fff;border-color:var(--teal)}
.portfolio-tab:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
/* Only the selected panel is in the page; the rest carry `hidden`. */
.portfolio-panel[hidden]{display:none}
.portfolio-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:22px}
.case{overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);background:#fff}
.case-visual{height:300px;background-size:cover;background-position:center}
/* Live-site preview: browser chrome above a homepage screenshot. Same 300px
   total as .case-visual so the grid rhythm is unchanged. */
.case-preview{border-bottom:1px solid var(--line);background:var(--soft)}
.case-preview-bar{height:34px;display:flex;align-items:center;gap:6px;padding:0 13px;background:#fff;border-bottom:1px solid var(--line)}
.case-url{flex:1;margin-left:8px;padding:4px 12px;border-radius:999px;background:var(--soft);color:var(--muted);font-size:10px;letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.case-shot{display:block;width:100%;height:266px;object-fit:cover;object-position:top center;background:var(--soft);transition:object-position 2.4s ease}
.case.hover-lift:hover .case-shot{object-position:bottom center}
.case-body{padding:25px}
.case-meta{display:flex;justify-content:space-between;gap:15px;color:var(--teal);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.14em}
.case h3{margin:9px 0 5px;font:400 28px/1.06 var(--serif)}
.case p{margin:0;color:var(--muted);font-size:13px}
.case-actions{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap}
/* Past mandates under the portfolio cards, drawn as a vertical timeline:
   a ringed node per entry, joined by a connector to the one below it. The
   connector lives on each item rather than the list, so the line stops at
   the last node instead of trailing past it. */
.history{margin-top:54px;padding-top:34px;border-top:1px solid var(--line)}
.history h3{margin:0;font:400 27px/1.1 var(--serif)}
.history-list{list-style:none;margin:28px 0 0;padding:0}
.history-list li{position:relative;display:grid;gap:18px 24px;padding:0 0 32px 42px}
/* An entry with the property's own photo runs image-beside-text; one
   without simply drops the media column. */
.history-list li.has-image{grid-template-columns:216px minmax(0,1fr);align-items:start}
.history-list .entry-media img{width:100%;height:158px;object-fit:cover;border-radius:var(--radius);background:var(--soft)}
.history-list li:last-child{padding-bottom:0}
.history-list li::before{content:"";position:absolute;left:1px;top:4px;width:14px;height:14px;border-radius:50%;
  background:#fff;border:3px solid var(--teal);box-shadow:0 0 0 4px rgba(4,141,112,.09)}
/* A live mandate reads as filled, one that has ended as an open ring. */
.history-list li.is-current::before{background:var(--teal)}
.history-list li::after{content:"";position:absolute;left:7px;top:24px;bottom:-4px;width:2px;background:var(--line)}
.history-list li:last-child::after{display:none}
.history-list .when{display:block;color:var(--teal);font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.history-list .what{display:block;margin:6px 0 3px;font:400 21px/1.24 var(--serif);color:var(--ink)}
.history-list li.is-current .what{margin-top:0}
.history-list .where{display:block;color:var(--muted);font-size:13px}
.history-list .note{margin:8px 0 0;max-width:62ch;color:var(--muted);font-size:14px}
.history-list .text-link{margin-top:12px;font-size:13px}
.history + .history{margin-top:44px;padding-top:0;border-top:0}

/* Reference templates, grouped by property type */
.template-group{margin-top:52px}
.template-group:first-of-type{margin-top:6px}
.template-group-head{display:flex;align-items:baseline;gap:22px;flex-wrap:wrap;padding-bottom:16px;margin-bottom:22px;border-bottom:1px solid var(--line)}
.template-group-head h3{margin:0;font:400 27px/1.1 var(--serif);white-space:nowrap}
.template-group-head p{margin:0;flex:1;min-width:260px;color:var(--muted);font-size:13px}
.template-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:22px}
.template-card{display:flex;flex-direction:column;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);background:#fff;transition:.25s ease}
.template-card:hover{transform:translateY(-4px);box-shadow:var(--shadow)}
.template-shot{position:relative;height:250px;background-size:cover;background-position:center}
.template-style{position:absolute;left:16px;bottom:16px;padding:7px 14px;border-radius:999px;background:rgba(255,255,255,.93);
  color:var(--navy);font-size:10px;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.template-body{display:flex;flex-direction:column;flex:1;padding:24px}
.template-body h4{margin:0 0 7px;font:400 25px/1.1 var(--serif)}
.template-body p{margin:0 0 20px;color:var(--muted);font-size:13px}
.template-body .button-link{margin-top:auto;align-self:flex-start}
/* About/Contact */
.contact-grid{display:grid;grid-template-columns:1fr 1fr;gap:50px}
.contact-card{padding:34px;border-radius:var(--radius);background:var(--soft);border:1px solid var(--line)}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.field{display:flex;flex-direction:column;gap:7px}
.field.full{grid-column:1/-1}
.field label{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.12em}
.field input,.field select,.field textarea{padding:13px 14px;border:1px solid var(--line);border-radius:var(--radius);font:14px var(--sans);color:var(--ink);background:#fff}
.field textarea{min-height:130px;resize:vertical}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{outline:2px solid var(--teal);outline-offset:1px;border-color:var(--teal)}
.field input[aria-invalid="true"],.field select[aria-invalid="true"],.field textarea[aria-invalid="true"]{border-color:#c2413a;background:#fffafa}
.field-error{margin:0;color:#c2413a;font-size:12px;font-weight:600}
.form-hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form-actions{display:flex;flex-wrap:wrap;gap:12px}
.form-actions .button-link{border:0;cursor:pointer;justify-content:center;flex:1 1 auto}
.form-actions .button-link.alt{border:1px solid var(--line)}
.form-actions .button-link.alt:hover{background:#fff;color:var(--teal)}
.form-note{margin:12px 0 0;color:var(--muted);font-size:12px}
.form-status{margin:12px 0 0;padding:12px 14px;border-radius:var(--radius);font-size:13px;font-weight:600;background:rgba(80,214,166,.16);border:1px solid rgba(4,141,112,.28);color:#065f4c}
.form-status.is-error{background:#fdf0ef;border-color:rgba(194,65,58,.32);color:#95302a}
/* Product */
.product-section{background:linear-gradient(120deg,#f7fbfa,#e9f6f3)}
.product-shell{display:grid;grid-template-columns:1fr 1fr;gap:50px;align-items:center}
.product-demo{padding:24px;border-radius:var(--radius);background:#fff;border:1px solid var(--line);box-shadow:var(--shadow)}
.product-bar{display:flex;justify-content:space-between;align-items:center;padding-bottom:12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:700}
.product-main{margin-top:16px;display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.product-stat{padding:14px;border-radius:var(--radius);background:var(--soft)}
.product-stat small{display:block;color:var(--muted);font-size:9px}
.product-stat strong{display:block;margin-top:3px;font-size:18px}
.chart{height:170px;margin-top:14px;display:flex;align-items:end;gap:7px;padding:16px;border-radius:var(--radius);background:#f7faf9}
.chart i{display:block;flex:1;border-radius:5px 5px 0 0;background:linear-gradient(180deg,var(--mint),var(--teal))}
/* CTA/footer */
.cta{position:relative;overflow:hidden;min-height:430px;display:flex;align-items:center;color:#fff}
.cta-bg{position:absolute;inset:0;background:linear-gradient(90deg,rgba(4,50,56,.8),rgba(4,50,56,.2)),var(--cta);background-position:center;background-size:cover}
.cta-inner{position:relative;z-index:1;display:flex;align-items:end;justify-content:space-between;gap:40px}
.cta h2{margin:0;max-width:800px;font:400 clamp(48px,6vw,86px)/.95 var(--serif);letter-spacing:-.05em}
.cta h2 em{color:#a9f3d6}
footer{padding:44px 0 28px}
.footer-top{display:grid;grid-template-columns:1.2fr 1fr 1fr;gap:40px;padding-bottom:36px;border-bottom:1px solid var(--line)}
.footer-brand img{width:150px}
.footer-brand p{max-width:300px;color:var(--muted);font-size:13px}
.footer-col h3{margin:0 0 13px;font-size:12px;text-transform:uppercase;letter-spacing:.15em}
.footer-col a{display:block;margin:8px 0;color:#536980;font-size:13px}
.footer-bottom{display:flex;justify-content:space-between;gap:20px;padding-top:20px;color:#6A7785;font-size:11px}
/* Motion */
.reveal{opacity:0;transform:translateY(24px);transition:.8s cubic-bezier(.2,.8,.2,1)}
.reveal.is-visible{opacity:1;transform:none}
/* Above the fold, nothing may wait for JavaScript to become visible.
   .reveal starts at opacity:0 and is switched on by the IntersectionObserver
   in js/main.js. That is right for sections further down the page, but the
   hero is already in view when the page loads — the observer fires for it
   immediately and the only real effect is that the headline cannot paint
   until the script has downloaded, parsed and run. First Contentful Paint
   waits on that.

   So the first screen paints at once and rises with a CSS animation instead.
   The animation moves `transform` only and never touches opacity, which is
   the point: the text is on screen and readable from the very first frame,
   and the movement is decoration on top of it rather than a gate in front
   of it. No JavaScript is involved, so it also survives a script that is
   slow, blocked or broken. */
.hero .reveal,
.interior-hero.reveal,
.portfolio-hero.reveal{opacity:1;transform:none;transition:none;
  animation:hero-rise .7s cubic-bezier(.2,.8,.2,1) both}
@keyframes hero-rise{from{transform:translateY(18px)}to{transform:none}}
/* Cookie consent.
   Sits above the floating WhatsApp button (z-index 120) so it cannot be
   half-hidden by it, and leaves room for it on the right at phone widths.
   Bottom-anchored rather than a modal: the visitor can read the site and
   answer when they like, which is also why it is a region and not a dialog. */
.consent{position:fixed;left:16px;right:16px;bottom:16px;z-index:130;
  display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap;
  max-width:760px;margin-inline:auto;padding:16px 18px;
  border:1px solid var(--line);border-radius:var(--radius);background:#fff;
  box-shadow:0 18px 50px rgba(11,35,64,.22);
  animation:consent-in .35s cubic-bezier(.2,.8,.2,1) both}
@keyframes consent-in{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.consent-copy{margin:0;flex:1 1 300px;color:var(--ink);font-size:13px;line-height:1.5}
.consent-actions{display:flex;align-items:center;gap:10px;flex:0 0 auto}
.consent-btn{padding:10px 18px;border-radius:999px;font-size:13px;font-weight:700;cursor:pointer;
  border:1px solid var(--line);background:#fff;color:var(--navy);transition:.2s ease}
.consent-btn-solid{background:var(--teal);border-color:var(--teal);color:#fff}
.consent-btn-plain:hover{background:var(--soft)}
.consent-btn-solid:hover{background:#03684f;border-color:#03684f}
.consent-btn:focus-visible{outline:3px solid var(--navy);outline-offset:2px}
@media(max-width:560px){
  /* Clear of the WhatsApp button, and the two answers share the width
     equally so neither is the accidental easy one to hit. */
  .consent{left:12px;right:12px;bottom:calc(82px + env(safe-area-inset-bottom,0px));padding:14px}
  .consent-actions{width:100%}
  .consent-btn{flex:1}
}
.hover-lift{transition:transform .3s ease,box-shadow .3s ease}
.hover-lift:hover{transform:translateY(-5px);box-shadow:var(--shadow)}
/* The nav carries long labels ("Hotel Business Intelligent"), so it moves to
   a menu well before tablet width — below this it can only be cramped. */
@media(max-width:1080px){
  .nav-toggle{display:flex}
  .nav-links{
    position:fixed;left:0;right:0;top:var(--header-h,77px);z-index:99;
    display:none;flex-direction:column;align-items:stretch;gap:0;
    max-height:calc(100vh - var(--header-h,77px));
    max-height:calc(100dvh - var(--header-h,77px));
    overflow-y:auto;overscroll-behavior:contain;
    padding:6px 20px calc(22px + env(safe-area-inset-bottom,0px));
    font-size:15px;background:#fff;border-top:1px solid var(--line);
    box-shadow:0 26px 50px rgba(11,35,64,.16)
  }
  .nav-links.open{display:flex}
  .nav-links>a,.nav-links summary{min-height:54px;padding:0 4px;border-radius:0;border-bottom:1px solid var(--line)}
  .nav-links>a:hover,.nav-links summary:hover{background:none}
  .nav-links details{position:static;border-bottom:1px solid var(--line)}
  .nav-links details summary{border-bottom:0}
  .nav-caret{margin-left:auto}
  .dropdown{position:static;min-width:0;margin:0 0 12px;padding:2px 0 2px 16px;border:0;border-left:2px solid var(--mint);border-radius:0;background:none;box-shadow:none}
  .dropdown a{min-height:48px;padding:0 8px;font-size:14px;color:#46617b}
  .nav-scrim{display:block;position:fixed;inset:0;z-index:98;background:rgba(11,35,64,.45);opacity:0;pointer-events:none;transition:opacity .25s ease}
  .nav-scrim.show{opacity:1;pointer-events:auto}
  body.nav-open{overflow:hidden}
}
@media(max-width:1050px){
  .services-head{grid-template-columns:1fr 1fr}
  .services-head>.text-link{grid-column:2;justify-self:end}
  .services-grid{grid-template-columns:repeat(2,1fr)}
  .service-card:nth-child(2){border-right:0}
  .service-card:nth-child(1),.service-card:nth-child(2){border-bottom:1px solid var(--line)}
  .project{grid-template-columns:1fr}
  .project-visual{min-height:500px}
  .why-grid,.contact-grid,.product-shell,.interior-grid{gap:42px}
}
@media(max-width:820px){
  .container{width:min(calc(100% - 32px),var(--max))}
  .hero{min-height:600px;align-items:end}
  /* Cap released: this is where the hero stacks to one column, and the copy
     stacked above the visual genuinely needs more than a screen. */
  .interior-hero{max-height:none}
  /* background-IMAGE, not the background shorthand: the shorthand resets every
     background longhand it does not mention, which would throw away the
     `auto,cover` sizing and `center` position set above and leave the photo at
     its natural 1920px pinned to the top-left corner — a magnified crop of one
     corner, tiled. Only the layers themselves change here, so the sizing and
     position keep coming from the base rule.
     The turn is that the scrim runs top-to-bottom instead of left-to-right,
     since the hero is one column at this width. */
  /* The ramp is keyed to where the copy actually lands: the heading starts at
     about 37% of the hero, so the wash has to be dense BEFORE that, not still
     climbing through it. The old stops were only 41% white at the heading's
     top edge, which put navy over raw photo — 3.2:1 on the dark pixels, and
     mottled, because each line crossed a different amount of scrim.
     The top fifth stays nearly clear, so the terraces still read as a photo. */
  /* var(--hero-sm, var(--hero)): a phone gets the small copy of the art, and
     any hero without one falls straight back to the full-size file, so this
     never depends on the -sm build having been run. The home hero is 432 KB
     at full size and 127 KB here. */
  .hero-bg{background-image:linear-gradient(180deg,rgba(255,255,255,.08) 0%,rgba(255,255,255,.14) 18%,rgba(255,255,255,.78) 38%,rgba(255,255,255,.90) 66%,#fff 100%),var(--hero-sm,var(--hero))}
  /* One column, so the scrim runs top-to-bottom instead of left-to-right —
     and unlike the sharp hero's version it stays strong the whole way down.
     That one can fade out at the top because its copy is pinned to the bottom
     of the hero; here the second grid item (a stats card, a visual) can push
     the text up into the fade, where navy on a dark photo stops being legible.
     The .80 floor holds the worst case near 9:1 wherever the text lands. */
  .hero-bg.is-blurred{background:none}
  /* Only the image layer is swapped; center/cover keeps coming from the
     shorthand in the base rule above. */
  .hero-bg.is-blurred::before{background-image:var(--hero-sm,var(--hero))}
  .hero-bg.is-blurred::after{background:linear-gradient(180deg,rgba(255,255,255,.90) 0%,rgba(255,255,255,.80) 40%,rgba(255,255,255,.86) 70%,rgba(255,255,255,.97) 100%)}
  .hero-inner{padding:70px 0 50px}
  .hero-grid,.interior-grid,.why-grid,.contact-grid,.product-shell,.list-grid{grid-template-columns:1fr}
  .services-head{grid-template-columns:1fr}
  .services-head>.text-link{grid-column:auto;justify-self:start}
  .services-grid,.portfolio-grid,.template-grid{grid-template-columns:1fr}
  .template-group{margin-top:36px}
  .template-group-head{display:block}
  .template-group-head h3{margin-bottom:8px;white-space:normal}
  .template-shot{height:200px}
  .service-card,.service-card:nth-child(2){border-right:0}
  .service-card{border-bottom:1px solid var(--line)}
  .service-card:last-child{border-bottom:0}
  .portfolio-head{flex-direction:column;align-items:start}
  .project-visual{min-height:450px}
  .project-meta{padding:35px}
  .detail-grid,.social-grid{grid-template-columns:1fr}
  .reel-grid{grid-template-columns:repeat(2,1fr);gap:12px}
  .platforms{grid-template-columns:repeat(2,1fr)}
  .cta{min-height:520px;align-items:end}
  .cta-inner{flex-direction:column;align-items:start;padding:70px 0;justify-content:flex-end}
  .footer-top{grid-template-columns:1fr}
  .footer-bottom{flex-direction:column}
  .form-grid{grid-template-columns:1fr}
  .field.full{grid-column:auto}

  /* --- Mobile sizing ------------------------------------------------
     Desktop uses 110px section rhythm and display type up to 108px; on a
     phone that pushes everything below the fold, so the vertical scale
     tightens and the type steps down. */
  /* .section and .detail-section follow --section-pad at every width now — at
     40px there is nothing left for a phone override to usefully tighten. */
  .interior-hero{padding:62px 0 56px}
  .hero-inner{padding:56px 0 46px}
  .display{font-size:clamp(38px,10.5vw,56px)}
  .section-title{font-size:clamp(30px,8.2vw,44px)}
  .cta h2{font-size:clamp(34px,9.2vw,52px)}
  .cta{min-height:400px}
  .cta-inner{padding:56px 0}
  .hero-copy,.interior-sub{font-size:15px}
  .scroll-mark{margin-top:32px}
  .services-head{gap:20px;margin-bottom:34px}
  .portfolio-head{gap:20px;margin-bottom:28px}
  .interior-visual{min-height:290px}
  .project-visual{min-height:340px}
  .project-meta{padding:28px}
  .case-visual{height:215px}
  .social-card img{height:235px}
  .service-card{min-height:0;padding:28px 4px}
  .service-card p{min-height:0}
  .detail-card{padding:26px}
  .detail-card h3{font-size:25px}
  .highlight{padding:28px 22px;border-radius:var(--radius)}
  .highlight-grid{margin-top:22px}
  .benefits{padding-left:0;border-left:0}
  .list-grid,.why-grid{gap:40px}
  .contact-card{padding:24px}
  /* Comfortable touch targets — text links are only 26px tall by default. */
  .text-link,.portfolio-tab{min-height:44px}
  .button-link{min-height:48px}
  .footer-col a{display:flex;align-items:center;min-height:44px}
  .brand{min-height:44px}
  .wa-float{right:16px;bottom:calc(16px + env(safe-area-inset-bottom,0px));width:54px;height:54px}
}
@media(max-width:480px){
  .brand img{width:118px}
  .hero{min-height:540px}
  .platforms{grid-template-columns:1fr 1fr}
  .rate-row{grid-template-columns:1.4fr 1fr .7fr .8fr;padding:12px 10px;font-size:11px}
  .product-demo{padding:20px}
  .case-actions{flex-direction:column;align-items:stretch}
  .case-actions .button-link{justify-content:center}
  .portfolio-tabs{gap:8px;margin:24px 0 36px}
  /* The timeline already stacks; just pull it in a little on a phone. */
  .history-list li{padding:0 0 26px 34px;gap:14px}
  .history-list li.has-image{grid-template-columns:1fr}
  .history-list .entry-media img{height:190px}
  .history-list .what{font-size:19px}
}
@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  .reveal{opacity:1;transform:none}
}


/* Featured-work slider. Every slide is rendered; only the active one shows.
   Stacked rather than a sliding track: the slides differ in copy length, and
   a track would change the section's height as it moved, jolting the page.
   Without JS the first slide carries .is-active from the server, so this
   degrades to a static card. */
.project-slide{display:none}
.project-slide.is-active{display:grid}

/* Parallax on slide change: the image travels further than the copy beside
   it, which is what reads as depth rather than as a plain crossfade. --dir
   is set by the script (1 forward, -1 back) so the movement follows the
   arrow that was pressed. */
@keyframes projectVisualIn{from{transform:translateX(calc(var(--dir,1) * 7%));opacity:.35}to{transform:none;opacity:1}}
@keyframes projectMetaIn{from{transform:translateX(calc(var(--dir,1) * 2.2%));opacity:0}to{transform:none;opacity:1}}
.project-slide.is-entering .project-visual{animation:projectVisualIn .62s cubic-bezier(.22,.7,.25,1) both}
.project-slide.is-entering .project-meta{animation:projectMetaIn .62s cubic-bezier(.22,.7,.25,1) .06s both}

@media (prefers-reduced-motion: reduce){
  /* The slide still changes, it just does not travel to get there. */
  .project-slide.is-entering .project-visual,
  .project-slide.is-entering .project-meta{animation:none}
}

/* Video fill for a slide that has a clip instead of a still. It sits behind
   the overlay card, covering the same area the background image did — the
   .browser-screen keeps that background as the layer beneath, so the poster
   and the photo agree while the file is still loading. */
.browser-screen{position:relative}
.project-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:0}
.browser-screen>.browser-card{position:relative;z-index:1}

/* Slider backdrop in black and white. Scoped to .project-slide on purpose:
   web-development.php uses .project-visual for the project's own screenshot,
   where the image is the content and must keep its colour.

   Painted on ::before rather than the element so the filter reaches only the
   backdrop — a filter on .project-visual would cascade into the browser
   mockup above it and grey out the video too. */
.project-slide .project-visual::before{
  content:"";position:absolute;inset:0;z-index:0;
  background-image:var(--project);background-size:cover;background-position:center;
  filter:grayscale(1);
}
.project-slide .project-visual>.browser{z-index:1}

/* Map embed in the About hero. The iframe is positioned rather than sized in
   flow because .interior-visual is a min-height box — an iframe at height:100%
   inside it would resolve against no definite height and collapse. */
.interior-visual--map{position:relative;background:var(--soft)}
.interior-visual--map iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}
/* Sits above the frame so it stays clickable, and gives a way out to the full
   map for anyone who cannot drive an embedded one. */
.map-open{position:absolute;left:14px;bottom:14px;z-index:1;padding:9px 14px;border-radius:999px;
  background:rgba(255,255,255,.94);color:var(--teal);font-size:12px;font-weight:700;
  box-shadow:0 6px 18px rgba(11,35,64,.16);transition:.25s ease}
.map-open:hover{background:#fff;transform:translateY(-2px)}
/* Language switcher — sits at the end of the nav where the CTA used to be.
   Two short links rather than a dropdown: with only two languages a menu
   costs a click and tells the visitor less than simply showing both. */
.lang-switch{display:flex;align-items:center;gap:2px;margin-left:10px;padding-left:12px;border-left:1px solid var(--line)}
.lang-switch a{display:flex;align-items:center;min-height:42px;padding:0 8px;border-radius:10px;font-size:12px;font-weight:700;color:#8092a6;transition:color .2s ease,background .2s ease}
.lang-switch a:hover{color:var(--teal);background:rgba(4,141,112,.07)}
.lang-switch a.is-current{color:var(--teal)}
@media(max-width:1080px){
  /* In the drawer the switcher is a row of its own under the links, not a
     trailing item on the same line. */
  .lang-switch{width:100%;margin:16px 0 0;padding:12px 0 0;border-left:0;border-top:1px solid var(--line)}
  .lang-switch a{min-height:44px;padding:0 12px;font-size:14px}
}
