/* SAFEUP public site — one design system for every marketing page.
 *
 * The homepage used to carry its own tokens inline (its own emerald, its
 * own blue, its own purple), so a visitor moving from the front page into
 * the booking flow crossed into a different brand. These are the same
 * values public/css/style.css uses for the logged-in product, so the two
 * halves of the site finally agree.
 *
 * Light by design: white and soft neutral ground, teal accents,
 * photography doing the heavy lifting. Dark panels are used sparingly and
 * on purpose, never as a whole-section backdrop.
 */

:root{
  /* LOCKED BRAND PALETTE. These six values are the whole system - no
     second brand colour, no second font. */
  --teal:#00CFCF;          /* signature accent, ~5% of the page */
  --teal-ink:#00363a;      /* text that has to sit ON teal */
  --teal-deep:#00b5b5;     /* hover only - the button deepens, never changes colour */
  --teal-wash:#e6fbfb;     /* the lightest teal, for quiet fills */
  --navy:#0B1720;          /* ~25% - headings, primary buttons, dark panels */
  --navy-soft:#16242f;
  --body:#52616B;          /* body copy */
  --white:#FFFFFF;
  --soft:#F5F8F8;          /* off-white ground, the 60% */
  --border:#E3E9EA;

  /* Names the rest of the sheet uses, mapped onto the locked palette so
     there is exactly one place a colour is decided. */
  --brand:var(--teal);
  --brand-strong:var(--navy);
  --brand-bright:var(--teal);
  --brand-wash:var(--teal-wash);
  --ink:var(--navy);
  --ink-soft:var(--navy-soft);
  --bg:var(--white);
  --bg-alt:var(--soft);
  --surface:var(--white);
  --text:var(--navy);
  --muted:var(--body);
  --muted-light:#8a94a3;
  --line:var(--border);
  --line-strong:#cdd7d9;
  --ok:#0f7a68;
  --warn:#b45309;

  /* Manrope only. ExtraBold for hero headlines, Bold for section
     headings, Medium for navigation, Regular/Medium for body. */
  --font:'Manrope',-apple-system,'Segoe UI',Roboto,sans-serif;

  --gap-xs:8px; --gap-sm:14px; --gap:20px; --gap-lg:32px; --gap-xl:56px;
  --section-y:88px;
  --r-sm:10px; --r:14px; --r-lg:22px; --r-pill:999px;

  --shadow-sm:0 1px 2px rgba(11,23,32,.05), 0 1px 3px rgba(11,23,32,.04);
  --shadow:0 2px 6px rgba(11,23,32,.05), 0 10px 24px -10px rgba(11,23,32,.14);
  --shadow-lg:0 4px 10px rgba(11,23,32,.06), 0 26px 54px -20px rgba(11,23,32,.22);
}

*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;}
body{background:var(--bg);color:var(--text);font-family:var(--font);
  -webkit-font-smoothing:antialiased;overflow-x:hidden;line-height:1.55;}
a{color:inherit;text-decoration:none;}
/* height:auto matters more than it looks.
   An <img> carries width and height attributes so the browser reserves
   the right space before the file arrives - but on an img those
   attributes map to the CSS width and height properties, so a photo
   given style="width:100%" in a 360px column was being drawn 360 wide
   and 675 tall: the full height attribute, squashed sideways. The
   tracking shot and the For Hotels / For Business figures were all
   distorted this way and it read as a slightly-off photograph rather
   than as a bug. auto restores the ratio; the attributes still reserve
   the space, because width and height together set an aspect-ratio. The
   rules below that set an explicit height (the photo cards, the journey
   steps) override this and are unaffected. */
img{display:block;max-width:100%;height:auto;}
/* THE hidden ATTRIBUTE HAS TO ACTUALLY HIDE.
   Its default is display:none from the UA sheet, which any rule with a
   display of its own beats - and most rows here have display:flex. So
   `el.hidden = true` set the attribute, the element stayed on screen,
   and the JS looked correct: /car-rental switched to Bus and showed the
   bus form ON TOP of the car form, both asking for a different booking.
   !important because the point is to beat whatever display the element
   was given. */
[hidden]{display:none!important;}
::selection{background:var(--brand-wash);color:var(--brand-strong);}
:focus-visible{outline:3px solid var(--brand);outline-offset:2px;border-radius:6px;}
@media (prefers-reduced-motion:reduce){ *{transition:none!important;animation:none!important;scroll-behavior:auto!important;} }

.wrap{max-width:1220px;margin:0 auto;padding:0 28px;}
@media (max-width:640px){ .wrap{padding:0 18px;} }

h1,h2,h3,h4{letter-spacing:-.03em;line-height:1.1;font-weight:700;color:var(--navy);}
h1{font-size:clamp(36px,5.6vw,62px);font-weight:800;letter-spacing:-.035em;}
h2{font-size:clamp(28px,3.9vw,44px);}
h3{font-size:20px;letter-spacing:-.02em;font-weight:700;}
.eyebrow,.badge,.bfield label,.field label{font-weight:600;}
p{color:var(--muted);}
.eyebrow{font-size:11.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--brand-strong);}
.lede{color:var(--body);font-size:16.5px;line-height:1.62;font-weight:400;}
body{font-weight:450;}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;}

/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;font-family:inherit;
  font-size:14.5px;font-weight:700;padding:0 26px;min-height:48px;border-radius:var(--r-pill);cursor:pointer;
  border:1.5px solid transparent;white-space:nowrap;
  transition:transform .16s ease,background .16s ease,color .16s ease,border-color .16s ease,box-shadow .16s ease;}
.btn:active{transform:translateY(1px);}
.btn-primary{background:var(--teal);color:var(--navy);box-shadow:0 6px 16px -9px rgba(0,207,207,.95);}
.btn-primary:hover{background:var(--teal-deep);transform:translateY(-2px);}
.btn-outline{background:var(--surface);color:var(--text);border-color:var(--line-strong);}
.btn-outline:hover{border-color:var(--teal);color:var(--navy);}
.btn-ghost{background:transparent;color:var(--navy);}
.btn-ghost:hover{background:var(--brand-wash);}
/* A borderless ghost button reads as a button against the page, but
   inside a bordered card it loses its edge and becomes a stray line of
   bold text - which is what "Salin Link" was on the affiliate panel. Here
   rather than in one page's own <style>, because that panel now appears
   on /affiliate and on /business and they must not diverge. */
.panel .btn-ghost{border:1px solid var(--line);}
.panel .code-box .btn-ghost{border-color:rgba(255,255,255,.35);}
.btn-sm{padding:0 18px;min-height:40px;font-size:13px;}
.btn-lg{padding:0 32px;min-height:52px;font-size:15.5px;}
.btn .arw{transition:transform .16s ease;}
.btn:hover .arw{transform:translateX(4px);}

/* ---------- header ---------- */
.site-header{position:sticky;top:0;z-index:60;background:rgba(255,255,255,.9);
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--line);}
.header-inner{display:flex;align-items:center;gap:22px;height:72px;}
.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:17px;letter-spacing:-.01em;flex:0 0 auto;}
/* NO RADIUS. The rounding existed to soften a dark plate baked into
   the old logo-icon.png; the mark is transparent now, so a radius would
   only clip the glyph's own edges. */
.brand img{width:30px;height:30px;}
.brand .up{color:var(--brand);}
.brand small{display:block;font-size:8px;letter-spacing:.19em;color:var(--muted-light);font-weight:600;text-transform:uppercase;line-height:1.4;}
/* SIX ITEMS, NOT FIVE.
   Car Rental joined the row and every label started wrapping inside
   itself - "Cara / Kerja", "Untuk / Hotel" - which reads as a broken
   header rather than a tight one. nowrap keeps each label on one line,
   and the gap tightens between 1040px and 1240px where the row is
   actually under pressure instead of being shrunk everywhere. */
.main-nav{display:flex;align-items:center;gap:26px;font-size:14.5px;font-weight:500;color:var(--muted);margin-left:10px;}
.main-nav a{white-space:nowrap;}
.main-nav a{padding:6px 0;border-bottom:2px solid transparent;}
.main-nav a:hover{color:var(--text);}
.main-nav a[aria-current="page"]{color:var(--text);font-weight:700;border-bottom-color:var(--brand);}
/* THE PROMO CHIP.
   Sits in the gap between the nav and the controls, which is otherwise
   dead space on a wide header. It is [hidden] in the markup and only
   revealed when a live promo comes back, so a site with no campaign
   running shows nothing at all rather than an empty slot.

   margin-left:auto pushes it to the right of the nav; .header-right
   keeps its own auto margin, so the chip lands between the two rather
   than pinned to either. */
.promo-chip{display:inline-flex;align-items:center;gap:6px;
  padding:7px 11px;border-radius:var(--r-pill);white-space:nowrap;
  background:var(--brand-wash,#e8faf6);border:1px solid rgba(0,207,207,.35);
  color:var(--brand-ink,#067c7c);font-size:13px;font-weight:600;
  text-decoration:none;transition:border-color .15s ease,transform .15s ease;
  /* THE BUDGET IS 204 PIXELS, and it is the same at every screen width -
     the header's contents are centred inside .wrap, so the gap between
     the nav and the controls does not grow with the monitor. Measured at
     1280, 1366, 1440, 1600 and 1920: 204px in Indonesian, 219 in
     Japanese, 233 in English.
     
     So the chip NEVER SHRINKS. Letting it (flex:0 1 auto) stopped the
     page overflowing and cut "hemat 25%" down to "he…", which is worse
     than no chip at all. It holds its content and the admin's text is
     capped instead, below. */
  flex:0 0 auto;min-width:0;}
.promo-chip:hover{border-color:var(--brand);transform:translateY(-1px);}
/* A drawn tag, not an emoji - public/js/noEmoji.test.js fails the build
   on one, and a chip that renders as a different glyph on every platform
   is not a brand mark.

   NO ARROW BESIDE IT. The chip is a pill with a tag on it and one word
   in it; nobody needs to be told it is a link. It also cost 20px, and
   20px is the difference between fitting in the Malay header - whose nav
   labels are the longest on the site, leaving 85px - and being withdrawn
   from it. */
.promo-chip svg{width:15px;height:15px;flex:none;stroke:currentColor;fill:none;stroke-width:1.7;
  stroke-linejoin:round;}
.promo-chip__k{font-weight:800;letter-spacing:.04em;text-transform:uppercase;font-size:11px;}
/* The admin's own copy, which can be any length. It gives way before the
   nav and the buttons do rather than pushing them off the edge. */
.header-right{display:flex;align-items:center;gap:11px;margin-left:auto;}
.lang-select{border:1px solid var(--line-strong);background:var(--surface);border-radius:var(--r-pill);
  padding:8px 12px;font-family:inherit;font-size:13px;color:var(--muted);cursor:pointer;}
.nav-toggle{display:none;background:none;border:1px solid var(--line-strong);border-radius:var(--r-sm);
  padding:9px 11px;cursor:pointer;color:var(--text);}
.nav-toggle svg{width:18px;height:18px;}
#mobile-nav{display:none;border-top:1px solid var(--line);background:var(--surface);}
#mobile-nav.open{display:block;}
#mobile-nav ul{list-style:none;padding:8px 0;}
#mobile-nav a{display:block;padding:13px 28px;font-size:15px;font-weight:600;}
#mobile-nav .mobile-promo{color:var(--brand-ink,#067c7c);background:var(--brand-wash,#e8faf6);}
@media (max-width:1240px){
  .header-inner{gap:12px;}
  .main-nav{gap:15px;font-size:13.6px;margin-left:0;}
  .header-right{gap:8px;}
  .header-right .btn-sm{padding:9px 14px;}
}
/* Below this the six labels plus a language picker plus two buttons stop
   fitting, and the first thing to give was the Book button running off
   the right edge - measured at 1100px. The login link goes instead: it
   is still in the mobile menu, and losing the primary action is worse
   than losing the secondary one. */
@media (max-width:1180px){
  .header-right .login-link{display:none;}
  /* No room beside a hamburger. fillPromoChip puts the same offer at the
     top of the mobile menu, so it is moved rather than lost - a promo
     push arrives mostly on phones. */
  .promo-chip{display:none;}
  .main-nav{gap:13px;}
}
@media (max-width:1040px){
  .main-nav{display:none;}
  .nav-toggle{display:inline-flex;}
  .header-right .login-link{display:none;}
}
@media (max-width:560px){
  .header-inner{gap:10px;height:62px;}
  .brand small{display:none;}
  .brand{font-size:15.5px;}
  .brand img{width:26px;height:26px;}
  .header-right{gap:8px;}
  .header-right .btn-sm{padding:9px 13px;font-size:12.5px;}
}
/* THE LANGUAGE SWITCHER STAYS ON A PHONE.
   It used to be hidden below 420px, which is most phones - an iPhone is
   390 - so a Japanese or Chinese visitor arriving on their phone had no
   way at all to change language, and the site simply looked broken to
   them. The room comes from Book Now instead: below 560px the fixed
   bottom bar already carries it as its primary action, so the header was
   spending its width on a second copy of a button that was never more
   than a thumb away. */
@media (max-width:560px){
  .header-right .btn-primary{display:none;}
  .lang-select{padding:7px 9px;font-size:12.5px;max-width:150px;}
}

/* Sarah has to clear the bottom bar.
   The launcher sits 14px from the bottom of the viewport, and below 820px
   that is underneath the fixed quick-actions bar - the bubble covered the
   Track tab and the bar covered the bubble. "body .x" rather than ".x" so
   this wins over the widget's own runtime stylesheet without !important. */
@media (max-width:820px){
  body .safeup-chat-launcher{bottom:calc(84px + env(safe-area-inset-bottom));}
  body .safeup-chat-hint{bottom:calc(97px + env(safe-area-inset-bottom));}
  body .safeup-chat-panel{bottom:calc(84px + env(safe-area-inset-bottom));
    height:min(70vh, calc(100vh - 160px));}
}

/* ---------- sections ---------- */
.section{padding:var(--section-y) 0;}
.section.tight{padding:56px 0;}
.section.alt{background:var(--bg-alt);}
.section-head{max-width:660px;margin-bottom:38px;}
.section-head h2{margin:12px 0 0;}
.section-head p{margin-top:14px;}
.section-head.center{margin-left:auto;margin-right:auto;text-align:center;}
.head-row{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap;margin-bottom:38px;}
.head-row .section-head{margin-bottom:0;}
@media (max-width:640px){ :root{--section-y:60px;} }

/* ---------- page hero (inner pages) ---------- */
.page-hero{padding:60px 0 48px;background:
  radial-gradient(90% 120% at 92% 0%,var(--brand-wash) 0%,rgba(232,250,246,0) 62%), var(--bg);}
.page-hero h1{max-width:15ch;}
.page-hero .lede{max-width:600px;margin-top:16px;}
.page-hero h1{margin-top:12px;}
.breadcrumbs{display:flex;gap:8px;font-size:13px;color:var(--muted-light);margin-bottom:18px;flex-wrap:wrap;}
.breadcrumbs a:hover{color:var(--brand-strong);}
.breadcrumbs span{color:var(--line-strong);}

/* ---------- editorial photo card ---------- */
.photo-card{display:flex;flex-direction:column;background:var(--white);
  border:1px solid var(--border);border-radius:20px;overflow:hidden;box-shadow:var(--shadow-sm);
  transition:transform .2s ease,box-shadow .2s ease,border-color .2s ease;}
.photo-card:hover{transform:translateY(-3px);box-shadow:var(--shadow);border-color:var(--line-strong);}
/* NOTHING IS CROPPED. `contain`, not `cover`, everywhere a commissioned
   photograph is shown - see the note above .steps for why this is a rule
   and not a preference. The frame keeps a fixed 16:9 so a row of cards
   lines up; a picture that is not 16:9 sits inside it on the navy, which
   is a mat, not a crop.
   The hover zoom went with it: scaling an image inside a frame with
   overflow:hidden shaves the edges off, which is the same thing this
   rule exists to stop. */
.photo-card .frame{position:relative;aspect-ratio:16/9;background:var(--navy);overflow:hidden;}
.photo-card .frame img{width:100%;height:100%;object-fit:contain;}
/* No photograph yet: a designed panel, never a grey box and never another
   service's picture. See PENDING_PHOTOGRAPHY in js/homeContent.js. */
.frame.no-photo{display:flex;align-items:center;justify-content:center;
  background:radial-gradient(120% 100% at 18% 4%,rgba(0,207,207,.30) 0%,rgba(0,207,207,0) 62%),
    linear-gradient(155deg,var(--navy-soft) 0%,var(--navy) 68%);}
.frame.no-photo span{font-size:38px;font-weight:900;letter-spacing:-.04em;color:rgba(255,255,255,.2);}
.photo-card .body{padding:18px 18px 20px;display:flex;align-items:flex-start;gap:12px;flex:1;}
.photo-card .body .txt{flex:1;min-width:0;}
.photo-card h3{font-size:16.5px;margin:0 0 5px;}
.photo-card p{font-size:13.5px;line-height:1.5;}
.photo-card .go{flex:0 0 auto;width:32px;height:32px;border-radius:50%;border:1.5px solid var(--line-strong);
  display:flex;align-items:center;justify-content:center;color:var(--brand-strong);font-size:14px;font-weight:700;
  transition:background .18s ease,color .18s ease,border-color .18s ease;}
.photo-card:hover .go{background:var(--teal);color:var(--navy);border-color:var(--teal);}

.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;}
.grid-4{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;}
.grid-2{display:grid;grid-template-columns:repeat(2,1fr);gap:22px;}
@media (max-width:980px){ .grid-3,.grid-4{grid-template-columns:repeat(2,1fr);} }
@media (max-width:680px){ .grid-3,.grid-4,.grid-2{grid-template-columns:1fr;} }

/* horizontal rail on phones, for rows that would otherwise be a long scroll */
@media (max-width:680px){
  .rail{display:flex;gap:14px;overflow-x:auto;scroll-snap-type:x mandatory;
    padding:0 18px 14px;margin:0 -18px;-webkit-overflow-scrolling:touch;}
  .rail > *{flex:0 0 76%;scroll-snap-align:start;}
}

/* ---------- feature list ---------- */
.features{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;}
.feature{display:flex;gap:12px;align-items:flex-start;padding:20px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r);}
.feature svg{width:22px;height:22px;flex:0 0 auto;color:var(--brand);}
.feature h4{font-size:14.5px;margin-bottom:4px;}
.feature p{font-size:13px;line-height:1.5;}
@media (max-width:900px){ .features{grid-template-columns:repeat(2,1fr);} }
@media (max-width:520px){ .features{grid-template-columns:1fr;} }

/* ---------- steps ---------- */
/* THE PHOTOGRAPH DECIDES THE BOX, NOT THE OTHER WAY ROUND.
   These four shots are 900x791 - almost square. In a 16:9 frame set to
   `cover` the browser threw away a third of every one of them, sideways,
   which is how a commissioned photograph ends up with somebody's arm
   outside the picture. The frame is now the shape the pictures actually
   are, and every frame on the site fits rather than crops. */
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:20px;}
.step{position:relative;}
.step .shot{aspect-ratio:9/8;border-radius:var(--r);overflow:hidden;background:var(--ink);margin-bottom:14px;}
.step .shot img{width:100%;height:100%;object-fit:contain;}
.step .n{font-size:12px;font-weight:800;letter-spacing:.14em;color:var(--brand);}
.step h3{font-size:17px;margin:6px 0 6px;}
.step p{font-size:14px;line-height:1.55;}
@media (max-width:860px){ .steps{grid-template-columns:repeat(2,1fr);} }
@media (max-width:520px){ .steps{grid-template-columns:1fr;} }

/* ---------- tracking ---------- */
/* The right column was 360px because it used to hold a phone mock-up,
   which is a portrait shape. It holds a wide photograph now - the crew,
   the bag and the app screen in one frame - and 360px squeezed it to a
   strip you could not read the phone in. Halves, so the picture carries
   the same weight as the words beside it. */
.track-panel{display:grid;grid-template-columns:1.02fr .98fr;gap:48px;align-items:center;}
.track-form{display:flex;gap:10px;margin:0 0 24px;flex-wrap:wrap;}
.track-form input{flex:1 1 220px;min-width:0;background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-pill);padding:14px 20px;font-family:inherit;font-size:15px;font-weight:600;color:var(--text);}
.track-form input:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 4px var(--brand-wash);}
.timeline{list-style:none;}
.timeline li{display:flex;gap:14px;padding:12px 0;align-items:flex-start;position:relative;}
.timeline li:not(:last-child)::before{content:"";position:absolute;left:8px;top:26px;bottom:-6px;width:2px;background:var(--line);}
.timeline .dot{width:18px;height:18px;border-radius:50%;border:2px solid var(--line-strong);background:var(--surface);
  flex:0 0 auto;display:flex;align-items:center;justify-content:center;font-size:10px;color:#fff;z-index:1;}
.timeline li.done .dot{background:var(--teal);border-color:var(--teal);color:var(--navy);}
.timeline li.done:not(:last-child)::before{background:var(--brand);}
.timeline .label{font-size:15px;font-weight:600;color:var(--muted-light);}
.timeline li.done .label{color:var(--text);}
.timeline .when{display:block;font-size:12.5px;color:var(--muted-light);font-weight:500;margin-top:2px;}
.phone{background:var(--ink);border-radius:34px;padding:10px;box-shadow:var(--shadow-lg);}
.phone-screen{background:var(--surface);border-radius:26px;padding:20px;}
.phone-row{display:flex;justify-content:space-between;gap:12px;padding:11px 0;border-bottom:1px solid var(--line);font-size:13.5px;}
.phone-row:last-child{border-bottom:0;}
.phone-row .k{color:var(--muted);font-weight:500;}
.phone-row .v{font-weight:700;text-align:right;}
.demo-note{margin-top:14px;font-size:12px;color:var(--muted-light);}
@media (max-width:900px){ .track-panel{grid-template-columns:1fr;gap:34px;} .phone{max-width:330px;} }

/* ---------- badges ---------- */
.badge{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:800;
  padding:5px 11px;border-radius:var(--r-pill);letter-spacing:.02em;}
.badge-ok{background:var(--teal-wash);color:var(--navy);}
.badge-wait{background:#fff4e5;color:var(--warn);}
.badge-muted{background:var(--bg-alt);color:var(--muted);}

/* ---------- forms ---------- */
.field{display:grid;gap:6px;margin-bottom:16px;}
.field label{font-size:12.5px;font-weight:700;color:var(--muted);}
.field input,.field select,.field textarea{width:100%;border:1px solid var(--border);border-radius:var(--r-sm);
  padding:13px 15px;font-family:inherit;font-size:15px;color:var(--navy);background:var(--soft);font-weight:500;}
.field input:focus,.field select:focus,.field textarea:focus{outline:none;border-color:var(--brand);
  box-shadow:0 0 0 4px var(--brand-wash);}
.field textarea{min-height:130px;resize:vertical;}

/* ---------- accordion ---------- */
.accordion{border-top:1px solid var(--line);}
.accordion details{border-bottom:1px solid var(--line);}
.accordion summary{list-style:none;cursor:pointer;padding:18px 40px 18px 0;position:relative;
  font-weight:700;font-size:15.5px;}
.accordion summary::-webkit-details-marker{display:none;}
.accordion summary::after{content:"+";position:absolute;right:8px;top:16px;font-size:20px;color:var(--brand);font-weight:600;}
.accordion details[open] summary::after{content:"−";}
.accordion .answer{padding:0 40px 18px 0;font-size:14.5px;color:var(--muted);line-height:1.6;}

/* ---------- footer ---------- */
.site-footer{background:var(--bg-alt);border-top:1px solid var(--line);padding:56px 0 30px;margin-top:0;}
.footer-grid{display:grid;grid-template-columns:1.6fr repeat(3,1fr);gap:34px;}
.site-footer h4{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--text);margin-bottom:14px;font-weight:800;}
.site-footer ul{list-style:none;display:grid;gap:10px;font-size:14px;}
.site-footer a{color:var(--muted);}
.site-footer a:hover{color:var(--brand-strong);}
.footer-brand{display:flex;align-items:center;gap:11px;font-weight:800;font-size:18px;}
.footer-brand img{width:32px;height:32px;}
.footer-tag{margin-top:10px;font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted-light);}
/* Partner With Us. A quiet card at the foot of every page rather than a
   banner competing with the thing the visitor came for - somebody who
   has read to the bottom is the one worth asking. */
.aff-widget{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  margin-top:36px;padding:18px 20px;border-radius:var(--r);
  background:linear-gradient(120deg,rgba(0,207,207,.12) 0%,rgba(0,207,207,.03) 70%);
  border:1px solid rgba(0,207,207,.28);transition:border-color .18s ease,transform .18s ease;}
.aff-widget:hover{border-color:var(--brand);transform:translateY(-1px);}
.aff-widget strong{display:block;font-size:15.5px;color:var(--text);}
.aff-widget span{font-size:13.5px;color:var(--muted);}
.aff-widget .aff-widget-cta{flex:0 0 auto;font-size:13.5px;font-weight:700;color:var(--brand-strong);}
.aff-widget .aff-widget-cta::after{content:" \2192";}
.footer-bottom{margin-top:40px;padding-top:20px;border-top:1px solid var(--line);font-size:13px;color:var(--muted-light);}
@media (max-width:820px){ .footer-grid{grid-template-columns:1fr 1fr;} }
@media (max-width:480px){ .footer-grid{grid-template-columns:1fr;} }

/* ---------- mobile bar ---------- */
.mobile-bar{display:none;}
@media (max-width:820px){
  .mobile-bar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:70;
    background:rgba(255,255,255,.96);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
    border-top:1px solid var(--line);padding:7px 6px calc(7px + env(safe-area-inset-bottom));}
  .mobile-bar a{flex:1;display:flex;flex-direction:column;align-items:center;gap:3px;
    font-size:10.5px;font-weight:600;color:var(--muted-light);padding:5px 2px;}
  .mobile-bar svg{width:21px;height:21px;}
  .mobile-bar a.current{color:var(--brand-strong);}
  .mobile-bar a.primary{color:#fff;}
  .mobile-bar a.primary svg{background:var(--teal);color:var(--navy);border-radius:50%;
    padding:7px;width:35px;height:35px;box-shadow:0 6px 14px -7px rgba(0,207,207,.95);}
  .mobile-bar a.primary span{color:var(--navy);}
  body{padding-bottom:74px;}
}

/* ---------- scroll reveal ---------- */
.reveal{opacity:0;transform:translateY(14px);transition:opacity .55s ease,transform .55s ease;}
.reveal.in{opacity:1;transform:none;}
@media (prefers-reduced-motion:reduce){ .reveal{opacity:1;transform:none;} }

/* ===== Service-page booking widget (js/serviceWidget.js) ===== */
/* The widget, in the same card language as the hero widget on the home
 page: one white card, fields in a row, the action on the right. It
 quotes nothing - the button carries the answers to the wizard, which
 is where a fare is worked out. */
.sw-card{background:var(--surface,#fff);border:1px solid var(--line,#e6eaee);
border-radius:var(--r-lg);box-shadow:var(--shadow-lg);padding:18px;margin-top:30px;}
.sw-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:0;}
.sw-f{display:flex;flex-direction:column;gap:5px;padding:10px 16px;
border-right:1px solid var(--line,#e6eaee);min-width:0;}
.sw-f:last-child{border-right:0;}
.sw-f label{font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
color:var(--muted,#6b7a86);}
.sw-f select,.sw-f input{border:0;background:transparent;font:inherit;font-size:15px;
font-weight:600;color:var(--text,#0b1720);padding:2px 0;width:100%;min-width:0;}
.sw-f select:focus,.sw-f input:focus{outline:2px solid var(--brand);outline-offset:3px;border-radius:4px;}
.sw-count{display:flex;align-items:center;gap:10px;}
.sw-count button{width:28px;height:28px;border-radius:50%;border:1px solid var(--line,#e6eaee);
background:transparent;font:inherit;font-size:15px;line-height:1;cursor:pointer;color:inherit;}
.sw-count button:hover{border-color:var(--brand);color:var(--brand);}
.sw-count output{font-size:15px;font-weight:700;min-width:1.2em;text-align:center;}
.sw-go{margin-top:0;width:100%;justify-content:center;}
/* Book and Save, in that order and in that weight. Saving is the quieter
   of the two on purpose: the widget exists to place an order, and an
   outline button beside a solid one says which is which without a word. */
.sw-actions{margin-top:14px;display:flex;flex-direction:column;gap:9px;}
.sw-save{width:100%;justify-content:center;gap:8px;}
.sw-save svg{width:16px;height:16px;flex:none;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linejoin:round;}
/* Filled once the plan is saved - the state of a heart is the whole
   reason it is a heart rather than the word. */
.sw-save.is-saved svg{fill:currentColor;}
.sw-save.is-saved{border-color:var(--brand);color:var(--brand-ink,#067c7c);}
/* role="status", so a screen reader announces it without the focus
   moving. Kept out of the flow until there is something to say. */
.sw-saved{margin:9px 0 0;font-size:12.5px;line-height:1.5;color:var(--brand-ink,#067c7c);}
.sw-saved.is-bad{color:#a3322c;}
@media (min-width:760px){
.sw{display:flex;align-items:stretch;gap:14px;flex-wrap:wrap;}
.sw-row{flex:1;}
.sw-actions{margin-top:0;align-self:center;flex-direction:row;}
.sw-go,.sw-save{width:auto;white-space:nowrap;}
/* Full width under the row, so a message never squeezes the fields. */
.sw-saved{flex:1 0 100%;margin-top:4px;}
}
/* Every field keeps a side gutter and the row stacks rather than
 scrolling sideways - this card is read on a phone more than anywhere
 else. */
@media (max-width:520px){
.sw-row{grid-template-columns:1fr;}
.sw-f{border-right:0;border-bottom:1px solid var(--line,#e6eaee);}
.sw-f:last-child{border-bottom:0;}
}

/* The kind of place and its address, side by side. On a phone they stack
   - a select and a street address squeezed into half a narrow screen are
   both unreadable. */
.sw-f--wide{grid-column:span 2;}
.sw-dest{display:flex;gap:8px;align-items:center;min-width:0;}
.sw-dest select{flex:0 0 auto;max-width:44%;}
.sw-dest input{flex:1 1 auto;min-width:0;}
@media (max-width:640px){
  .sw-f--wide{grid-column:span 1;}
  .sw-dest{flex-direction:column;align-items:stretch;gap:4px;}
  .sw-dest select{max-width:none;}
}

/* Which end the airport is on. Direction is not a question a traveller
   answers in words - it is which of the two ends is the airport, and this
   says it. On a phone it sits inline rather than floating between two
   columns that are no longer side by side. */
.sw-swap{width:34px;height:34px;border-radius:50%;border:1px solid var(--line,#e6eaee);
  background:var(--surface,#fff);color:var(--brand);font-size:16px;line-height:1;
  cursor:pointer;flex:none;align-self:center;transition:all .15s ease;}
.sw-swap:hover{border-color:var(--brand);transform:rotate(180deg);}
.sw-swap[aria-pressed="true"]{background:var(--brand);color:#fff;border-color:var(--brand);}
@media (max-width:760px){ .sw-swap{align-self:flex-start;margin:8px 0 0 16px;} }

/* The operator under a partner's vehicle. Quiet, because the car is what
   the customer came for - but present, because four identical Alphards at
   four prices is a choice between four companies. */
.car-operator{margin:6px 0 0;font-size:12.5px;font-weight:600;color:var(--muted);}

/* The five groups SAFEUP sells under, as a rail of doors above the flat
   service list. Wraps on a phone rather than scrolling sideways - five
   tiles fit two-up without the thumb having to drag. */
.grp-rail{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px;margin-top:26px;}
.grp{display:flex;flex-direction:column;align-items:center;gap:12px;text-decoration:none;
  color:inherit;background:var(--surface,#fff);border:1px solid var(--line,#e6eaee);
  border-radius:var(--r-lg);padding:14px;transition:all .18s ease;}
.grp:hover{transform:translateY(-2px);box-shadow:var(--shadow-lg);border-color:var(--brand);}
/* THE FRAME FITS THE PICTURE, never the other way round. These are
   commissioned posters, some of them wide - a square tile with `cover`
   would have thrown away the half that carries the artwork. A 16:9 band
   the full width of the tile holds them whole, with `contain` doing the
   fitting and the navy behind showing as a thin mat at most. */
.grp__i{width:100%;aspect-ratio:16/9;max-width:100%;border-radius:12px;overflow:hidden;
  /* White, not navy: a letterboxed poster should mat in the page's own
     colour rather than punch a dark rectangle into it. */
  background:#fff;flex:none;}
.grp__i img{width:100%;height:100%;object-fit:contain;display:block;}
.grp__t{font-size:14px;font-weight:700;text-align:center;line-height:1.3;}
@media (max-width:560px){ .grp-rail{grid-template-columns:repeat(2,1fr);} }
