/* ==========================================================================
   Casper Tours — guided island tours, Willemstad, Curaçao

   THE BRIEF. Their live site is a client-rendered React app. The HTML that
   reaches a crawler, a WhatsApp link preview or a phone on bad hotel wifi is
   5,910 bytes containing ZERO words, ZERO links and no h1. It scores 0/100.
   Behind the JavaScript there is a real business with three priced tours, 64
   hand-written FAQ answers and a cruise-port pick-up nobody can find.

   So this is not a writing job either. It is a RENDERING job: the same
   business, as HTML that exists before any script runs. Everything on this
   page is in the source. Block the stylesheet and the script and the three
   tours, the route of each one, the telephone number and the enquiry form are
   all still there.

   THE AUDIENCE DECIDES THE SHAPE. Their own FAQ is written for cruise
   passengers — Cruise Gate 2, "back to your ship on time", "what if my ship
   is delayed". A cruise visitor has a deadline measured in hours and is
   reading on a phone at the terminal. So: the cruise block sits ABOVE the
   tour grid, the telephone number is never more than one screen away, and
   three tours means the comparison fits on one screen instead of nine.

   POPPIER AND SIMPLER, which is the client's brief on all three of these
   demos, read in that order of authority:

     SIMPLER first. Seven sections. One eyebrow label on the whole page. No
     bordered cards — a card here is a fill and a photograph. The 64 FAQ
     answers are on the page but folded into one accordion rather than
     sprayed across six blocks.

     POPPIER second. The heaviest type in this folder: the h1 at 900 up to
     5.5rem. The solid button is their own logo yellow with their own navy on
     it, unchanged in both themes. The conversion band is a full-width slab of
     that yellow.

     FLAIR last, and only from Curaçao and from their own logo.

   COLOUR IS MEASURED, NOT CHOSEN. Every value below was sampled with sharp
   out of a file in img/originals/, and the pixel count is the evidence.

     THEIR LOGO — 31d9aace…png, the roundel centre, 664x664 sampled
       #EBE136  Casper yellow   12,106px  37.4%   the disc. The brand.
       #25356B  bus navy         4,963px  15.3%   the minibus
       #6D66C5  ring violet      3,921px  12.1%   sampled at the left edge

     THE HANDELSKADE — b560dcc…png, the façade row at y460, 70px deep
       #066BC7  cobalt          the blue house        461px  12%
       #EAB8C7  rose                                  703px  25%
       #F6E3BA  cream                                 677px  22%
       #A9B4D8  lilac                                 249px  10%
       #24685C  deep green                            214px   7%
       #F8CBA9  peach                                 346px  10%
       #C65928  roof tile       y400, 700px wide      542px   3%

     THE WATER
       #78B7ED  the open sea beyond the Handelskade   927px  25.8%
       #1677A8  the Sint Annabaai under the bridge    113px   1.4%
       #58B6C7  the shallows, west-coast photograph   857px   4.8%
       #D5CAB6  the sand, same photograph          10,827px  66.8%

   Those fourteen are FILLS. None of them carries text on a light ground
   except the two that were checked for it. Text takes derived values, and
   every pair below is computed in BOTH themes:

     LIGHT   --ink #101A33 on --ground #FEFDF1        16.74 : 1
             --ink-2 #3A4763                           9.08 : 1
             --muted #5A6880                           5.51 : 1
             --act #25356B (their bus navy)           11.32 : 1
             --accent #4F45A0 (the ring violet, dark)  7.63 : 1
             --tile #9B3A11 (the roof tile, darkened)  6.83 : 1
     DARK    --ink #F2F4FA on --ground #10162D        16.26 : 1
             --ink-2 #C6CDE2                          11.26 : 1
             --muted #98A2BE                           7.02 : 1
             --act #EBE136 (the yellow takes over)    13.07 : 1
             --accent #A8A2F0                          7.74 : 1
             --tile #F8A87E                            9.27 : 1
     THE BAND, light   --band-ink #101A33 on #EBE136  12.61 : 1
                       --band-2   #3E3A1B on #EBE136   8.41 : 1

   ONE DELIBERATE FIXED PAIR. --cta / --cta-on is their logo yellow with their
   logo navy on it, 12.61 : 1, and it does NOT flip with the theme. A logo
   does not change colour because somebody set their phone to dark, and a
   fixed pair cannot produce the failure this folder keeps hitting — a token
   colour landing on a fixed ground. It is used for the solid buttons only.

   The chat accent is a SEPARATE token and it is themed, because the shared
   chat CSS puts --chat-accent on as TEXT (.chat__handoff) against
   --chat-surface. Yellow text on white is 1.24 : 1. So --chat-accent maps to
   --act, not to --cta: navy on white 11.67 in light, yellow on #18213F 11.56
   in dark. Two tokens that look like one thing and are not — that is written
   here because it is exactly the kind of mapping that gets "tidied" later.

   Light is the design. Dark is the option somebody opts into. An island tour
   page is read outdoors, in daylight, on a phone.

   TYPE — three faces, one job each, and the job is written here:

     Outfit          headings, the brand wordmark, and nothing else. Ever.
                     900 for the h1, 700 under it.
     Manrope         everything read at length, every label, every button.
     IBM Plex Mono   FIGURES ONLY — .fig. Prices, durations, clock times, the
                     telephone number. Twenty-odd elements on the page against
                     several hundred of the body face.

   The mono face is confined to .fig for the reason TDS failed review: there a
   monospace had quietly become the default for fifteen different LABEL roles
   and the verdict was "too many elements fighting for attention". Three tours
   whose durations are 4 h, 6 h and 6–7 h genuinely read better as
   measurements. A label does not. If .fig ever appears on something that is
   not a number, take it off.
   ========================================================================== */

:root {
  color-scheme: light;

  /* ---- the fourteen measured colours. FILLS ONLY. ---- */
  --casper-yellow: #EBE136;
  --casper-navy:   #25356B;
  --casper-violet: #6D66C5;
  --kade-cobalt:   #066BC7;
  --kade-rose:     #EAB8C7;
  --kade-cream:    #F6E3BA;
  --kade-lilac:    #A9B4D8;
  --kade-green:    #24685C;
  --kade-peach:    #F8CBA9;
  --kade-tile:     #C65928;
  --sea-open:      #78B7ED;
  --annabaai:      #1677A8;
  --knip:          #58B6C7;
  --sand:          #D5CAB6;

  /* ---- the working set, light ---- */
  --ground:    #FEFDF1;   /* their logo yellow at 7% over white */
  --surface:   #FFFFFF;
  --surface-2: #FBF8D3;   /* the same yellow at 22% */
  --ink:       #101A33;
  --ink-2:     #3A4763;
  --muted:     #5A6880;
  --line:      #E2E3E0;
  --line-2:    #C1C5CB;

  --act:       #25356B;   /* action, as TEXT and rules. 11.32 on --ground */
  --act-hover: #1B2A57;
  --act-on:    #FFFFFF;   /* 11.67 on --act */
  --accent:    #4F45A0;
  --tile:      #9B3A11;

  /* The fixed pair. Their logo, and it never flips. See the header. */
  --cta:       #EBE136;
  --cta-on:    #101A33;
  --cta-hover: #F5EC63;

  /* The conversion band: a full slab of their yellow. A THEMED PAIR, not a
     fixed colour with --ink on top of it. */
  --band:      #EBE136;
  --band-ink:  #101A33;
  --band-2:    #3E3A1B;
  --band-line: #C9C02D;

  --good:      #14654A;
  --warn:      #9B3A11;

  --display: "Outfit", "Segoe UI", system-ui, sans-serif;
  --body:    "Manrope", "Segoe UI", system-ui, -apple-system, sans-serif;
  --fig:     "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* The whole scale is declared here, including every step the shared chat
     block reads. font-size INHERITS, so an undefined step does not fall back
     to a default — it falls back to the PARENT SIZE, and the chat panel
     silently renders flat with no hierarchy at all. That is exactly what
     happened on Elegance and on Body Fit. */
  --f-100: .75rem;    --f-200: .8125rem;  --f-300: .875rem;
  --f-400: .9375rem;  --f-500: 1rem;      --f-600: 1.125rem;
  --f-700: 1.25rem;   --f-800: 1.5rem;    --f-900: 1.875rem;
  --f-1000: 2.5rem;

  --ls-label: .14em;
  --ls-caps:  .04em;

  --pad: clamp(1.25rem, 4vw, 3rem);
  --gap: clamp(3.5rem, 6.5vw, 5.5rem);
  --wrap: 76rem;
  --r: 16px;
  --r-sm: 9px;

  /* The Handelskade, as a rule. Seven façade colours measured off their own
     photograph of it, hard-edged, no blending. It appears exactly twice — the
     foot of the hero and the head of the footer — and it carries no text in
     either theme, which is why it can use the measured values raw. */
  --kade: linear-gradient(90deg,
    var(--kade-cream)  0 14.2857%, var(--kade-cobalt) 0 28.5714%,
    var(--kade-green)  0 42.8571%, var(--kade-rose)   0 57.1428%,
    var(--kade-lilac)  0 71.4285%, var(--kade-peach)  0 85.7142%,
    var(--kade-tile)   0 100%);
}

/* Dark is opted into, never sniffed from the OS — the toggle writes
   data-theme and it is remembered. The hero keeps its own fixed dark scrim
   either way, because a photograph needs one.

   The fourteen measured colours do NOT flip. They are photographs and a
   painted façade is the colour it is painted regardless of a phone setting.
   Only the derived text colours flip — and --act flips all the way from the
   navy to the yellow, because navy on a navy-black ground is nothing. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --ground:    #10162D;
  --surface:   #18213F;
  --surface-2: #222D52;
  --ink:       #F2F4FA;
  --ink-2:     #C6CDE2;
  --muted:     #98A2BE;
  --line:      #2A3559;
  --line-2:    #3D4A74;

  --act:       #EBE136;   /* 13.07 on --ground, 11.56 on --surface */
  --act-hover: #F5EC63;
  --act-on:    #10162D;   /* 13.07 on --act */
  --accent:    #A8A2F0;
  --tile:      #F8A87E;

  --band:      #18213F;
  --band-ink:  #F2F4FA;
  --band-2:    #C6CDE2;
  --band-line: #3D4A74;

  --good:      #63C99D;
  --warn:      #F0A46C;
}

*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ground); color: var(--ink);
  font-family: var(--body); font-size: var(--f-500); line-height: 1.66;
  -webkit-font-smoothing: antialiased;
}

img { max-inline-size: 100%; block-size: auto; display: block; }
a { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 3px; }
:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 3px solid var(--act); outline-offset: 3px; border-radius: 4px;
}

h1, h2, h3 { font-family: var(--display); font-weight: 700; margin: 0; text-wrap: balance; }
.h-hero { font-size: clamp(2.9rem, 6.8vw, 5.5rem); line-height: .96; letter-spacing: -.04em; font-weight: 900; }
.h-lg   { font-size: clamp(2rem, 4.3vw, 3.25rem); line-height: 1.04; letter-spacing: -.034em; font-weight: 800; }
/* There is deliberately no third heading class. Every h3 on this page belongs
   to a component — .pillar, .tour, .trust, .ftr__h — and sizes there. A
   general-purpose .h-sm existed in the first draft, was used nowhere, and is
   exactly the sort of spare utility that later gets applied to something for
   no reason. */

p, li, dd { text-wrap: pretty; }
p { margin: 0; }

/* The one job of the mono face. Nothing but numbers. */
.fig { font-family: var(--fig); font-variant-numeric: tabular-nums; letter-spacing: -.02em; }

.skip { position: absolute; inset-inline-start: -9999px; inset-block-start: 0; z-index: 200;
        background: var(--cta); color: var(--cta-on); padding: .8rem 1.2rem; font-weight: 800; }
.skip:focus { inset-inline-start: 0; }
.wrap { inline-size: 100%; max-inline-size: var(--wrap); margin-inline: auto; padding-inline: var(--pad); }

/* ONE on the whole page, over the hero, and it carries facts the h1 does not
   repeat. Every other eyebrow was cut: a small-caps line saying in miniature
   what the h2 under it says in full is the ceremony ../../CLAUDE.md calls the
   tell of a 2010s page. */
.label {
  font-family: var(--body); font-weight: 800; font-size: var(--f-300);
  letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--act);
}
.note { margin-block-start: clamp(1.5rem, 2.8vw, 2rem); font-size: var(--f-400); color: var(--muted); max-inline-size: 64ch; }

/* Honeypot. Off-screen rather than display:none — a bot that renders the page
   skips a hidden field and fills a visible one. */
.hp { position: absolute; inset-inline-start: -9999px; inline-size: 1px; overflow: hidden; }

/* ---------- header ----------
   Breakpoint COMPUTED from the parts, not guessed at a round number:
     brand  mark 40 + gap 10 + "Casper Tours" at 20px/800 Outfit ≈ 145  = 195
     nav    "Tours" 48 + "Cruise visitors" 118 + "Good to know" 108
            + 2 gaps at 1.6rem                                          = 325
     lang   EN | ES                                                     =  70
     theme                                                              =  34
     CTA    "Ask for a tour" at 14px/800 + 2.2rem padding               = 145
     2 flex gaps at 1.75rem                                             =  56
     padding-inline 2 x 48                                              =  96
                                                                 total   921

   1040 leaves ~13% of air over the computed total before the nav drops to its
   own scrolling row. NOTE, honestly: these advances are calculated from the
   type metrics, NOT measured in a rendered browser — nothing here has been
   opened in one. The margin is deliberately wider than Gone Caribe's for
   exactly that reason. */

.hdr {
  position: sticky; inset-block-start: 0; z-index: 60;
  background: color-mix(in srgb, var(--ground) 86%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-block-end: 1px solid var(--line);
}
.hdr .wrap { display: flex; align-items: center; gap: clamp(.75rem, 2.2vw, 1.75rem); min-block-size: 68px; }

.brand { flex: none; display: inline-flex; align-items: center; gap: .6rem; text-decoration: none; }
/* One mark, both themes. It is a saturated yellow disc, not line art — it
   reads on #10162D exactly as well as on #FEFDF1, so there is no light
   variant to get out of sync. */
.brand__mark { inline-size: 42px; block-size: 42px; }
.brand__name { font-family: var(--display); font-weight: 800; font-size: var(--f-700); letter-spacing: -.03em; }

.nav { display: flex; gap: clamp(.9rem, 1.7vw, 1.6rem); margin-inline-start: auto; }
.nav a { font-size: var(--f-500); font-weight: 600; color: var(--ink-2); text-decoration: none; padding-block: .3rem; position: relative; white-space: nowrap; }
.nav a::after {
  content: ""; position: absolute; inset-inline: 0; inset-block-end: -2px; block-size: 3px;
  background: var(--act); scale: 0 1; transform-origin: left;
  transition: scale .28s cubic-bezier(.2,.7,.3,1);
}
.nav a:hover { color: var(--ink); }
.nav a:hover::after, .nav a[aria-current="true"]::after { scale: 1 1; }
.nav a[aria-current="true"] { color: var(--ink); }

.hdr__tools { display: flex; align-items: center; gap: .5rem; flex: none; }
.lang { display: inline-flex; gap: .1rem; font-size: var(--f-200); font-weight: 800; letter-spacing: .06em; }
.lang a { padding: .3rem .45rem; border-radius: 6px; text-decoration: none; color: var(--muted); }
.lang a:hover { color: var(--ink); }
.lang a[aria-current="page"] { background: var(--surface-2); color: var(--ink); }
:root[data-theme="dark"] .lang a[aria-current="page"] { background: var(--surface-2); }

.icon-btn {
  inline-size: 34px; block-size: 34px; display: grid; place-items: center;
  background: transparent; color: var(--muted); border: 0; border-radius: 50%; cursor: pointer;
  transition: color .2s ease, background .2s ease;
}
.icon-btn:hover { color: var(--ink); background: var(--surface-2); }
.icon-btn svg { inline-size: 18px; block-size: 18px; }

@media (max-width: 1040px) {
  .hdr .wrap { flex-wrap: wrap; min-block-size: 60px; padding-block: .5rem; }
  .nav { order: 3; inline-size: 100%; margin-inline-start: 0; overflow-x: auto; scrollbar-width: none; gap: 1.3rem; padding-block-end: .3rem; }
  .nav::-webkit-scrollbar { display: none; }
  .hdr__tools { margin-inline-start: auto; }
}

/* On a phone the wordmark goes and the roundel stays. At 390 the mark, the
   wordmark, two language links, the theme button and the CTA come to roughly
   440px against 350px of usable width, so the header would wrap to three
   sticky rows. Dropping the wordmark takes it to two. Nothing is lost to a
   screen reader: the link already carries aria-label="Casper Tours, home",
   and with no visible text there is no label-in-name conflict to create. */
@media (max-width: 540px) {
  .brand__name { display: none; }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--body); font-weight: 800; font-size: var(--f-500);
  padding: .88rem 1.6rem; border-radius: 999px;
  border: 2px solid transparent; cursor: pointer; text-decoration: none;
  transition: background .2s ease, color .2s ease, border-color .2s ease, translate .2s ease;
}
/* Their logo, unchanged in both themes. 12.61 : 1. */
.btn--solid { background: var(--cta); color: var(--cta-on); }
.btn--solid:hover { background: var(--cta-hover); translate: 0 -2px; }
.btn--ghost { border-color: color-mix(in srgb, currentColor 34%, transparent); }
.btn--ghost:hover { border-color: currentColor; }
.btn--sm { padding: .55rem 1.1rem; font-size: var(--f-300); border-width: 2px; }

/* ---------- sections ---------- */

.sec { padding-block: var(--gap); }
.sec--tight { padding-block: clamp(2.5rem, 4.5vw, 3.5rem); }
.sec--raise { background: var(--surface); border-block: 1px solid var(--line); }
/* The tour grid only. Its cards have no borders, so the cards are white and
   the ground under them is the warm one — otherwise three borderless white
   panels on near-white paper stop being three things. */
.sec--sand { background: var(--surface-2); border-block: 1px solid var(--line); }
/* The one full-strength colour block on the page, and it is at the
   conversion point. */
.sec--band { background: var(--band); color: var(--band-ink); border-block: 1px solid var(--band-line); }
.sec__head { max-inline-size: 44rem; margin-block-end: clamp(2rem, 3.4vw, 3rem); }
.sec__head h2 + .lede { margin-block-start: 1.15rem; }
.lede { font-size: var(--f-700); color: var(--ink-2); line-height: 1.55; max-inline-size: 60ch; }
.sec--band .lede { color: var(--band-2); }

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

.hero { position: relative; isolation: isolate; }
.hero__pic { position: absolute; inset: 0; z-index: -2; }
.hero__pic img { inline-size: 100%; block-size: 100%; object-fit: cover; object-position: 62% 46%; }

/* The scrim is FIXED, not themed. Their aerial is bright sky, pale water and
   a white ship; light type on a darkened frame reads in both themes and a
   pale veil with dark type over it does not.

   ONE LAYER, horizontal only: dark under the copy column, clear over the
   right-hand third where the ship is. There is deliberately no vertical fade
   into the page ground — the hero is a self-contained dark block that meets
   the paper below it on a clean edge, with the kade stripe on that edge. A
   vertical fade is how the last build in this folder put near-white type onto
   pale limestone.

   THE ALPHAS ARE COMPUTED. The scrim is rgb(16 22 45), their own bus navy
   taken to the darkness a scrim needs. The worst pixel this photograph can
   put under the type is a white cloud, so every alpha was checked against
   #FFFFFF:

     alpha  composite over #FFFFFF   #FFFFFF text   --casper-yellow #EBE136
      .88        #26293C                12.5 : 1          8.6 : 1
      .82        #363949                10.3 : 1          7.1 : 1
      .74        #4A4C59                 7.6 : 1          5.2 : 1

   All the type sits inside .wrap, capped at 76rem and centred, and the widest
   line in it (the h1 at 16ch) ends around 44% of a 1440 viewport. So the
   gradient holds .82 or better to 48% and only then falls away over the
   water and the ship, where nothing is written. */
.hero::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg,
    rgb(16 22 45 / .90) 0%,
    rgb(16 22 45 / .86) 30%,
    rgb(16 22 45 / .82) 48%,
    rgb(16 22 45 / .46) 66%,
    rgb(16 22 45 / .16) 84%,
    rgb(16 22 45 / .05) 100%);
}

/* The Handelskade, on the edge where the photograph meets the paper. No
   markup, no text on it, both themes identical — it is a picture, not a
   surface. */
.hero::before {
  content: ""; position: absolute; z-index: 1;
  inset-inline: 0; inset-block-end: 0; block-size: 9px;
  background: var(--kade);
}

@media (max-width: 900px) {
  /* No room for a clear half on a phone, so the scrim goes vertical — and
     still never falls below .82 anywhere type can reach. */
  .hero::after { background: linear-gradient(180deg, rgb(16 22 45 / .90) 0%, rgb(16 22 45 / .82) 55%, rgb(16 22 45 / .88) 100%); }
  .hero__pic img { object-position: 70% 46%; }
}

.hero .wrap { padding-block: clamp(4.5rem, 12vw, 8rem) clamp(3.25rem, 7vw, 5rem); min-block-size: min(76svh, 660px); display: flex; flex-direction: column; justify-content: center; }
.hero h1 { margin-block-start: 1.1rem; max-inline-size: 16ch; }
.hero h1 em { font-style: normal; display: block; }
.hero__sub { margin-block-start: 1.5rem; max-inline-size: 50ch; font-size: var(--f-700); line-height: 1.5; }
.hero__cta { display: flex; gap: .7rem; flex-wrap: wrap; margin-block-start: 2rem; }

/* What is left of a fact list: one line. It does NOT carry a price. Their own
   site gives two different adult figures for the cheapest tour, so no "from"
   number can honestly lead this page — see the README. What leads instead is
   the thing that actually decides it for a cruise passenger. */
.hero__fact { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .7rem; margin-block-start: clamp(1.75rem, 3vw, 2.25rem); }
.hero__fact b { font-family: var(--display); font-size: var(--f-800); font-weight: 800; line-height: 1.1; letter-spacing: -.02em; }
.hero__fact span { font-size: var(--f-400); }

/* These MUST come after the rules above. The hero sits on a FIXED dark scrim,
   so its type takes fixed light values rather than theme tokens — and these
   are single-class selectors, so at equal specificity source order is the
   whole fight. Placed before, the block is dead code and the copy renders in
   --ink-2: dark slate on a dark scrim. That exact bug has shipped in this
   folder before. */
.hero .wrap { color: #FFFFFF; }
.hero .label { color: #78B7ED; }          /* the measured open sea, raw */
.hero h1 em { color: #EBE136; }           /* their logo yellow, raw */
.hero__sub { color: #DDE2F0; }
.hero__fact b { color: #EBE136; }
.hero__fact span { color: #C3CBE0; }
.hero .btn--ghost { color: #FFFFFF; border-color: rgb(255 255 255 / .5); }
.hero .btn--ghost:hover { border-color: #FFFFFF; background: rgb(255 255 255 / .1); }

@media (prefers-reduced-motion: no-preference) {
  .hero .label, .hero h1, .hero__sub, .hero__cta, .hero__fact { animation: rise .6s cubic-bezier(.2,.7,.3,1) both; }
  .hero h1 { animation-delay: .05s; }
  .hero__sub { animation-delay: .1s; }
  .hero__cta { animation-delay: .15s; }
  .hero__fact { animation-delay: .2s; }
  @keyframes rise { from { opacity: 0; translate: 0 12px; } to { opacity: 1; translate: 0 0; } }
}

/* ---------- the four things true of every tour ----------
   Four bordered cards would have been the obvious move and would have been
   four more boxes. Each fact stands under one thick rule in one of the
   measured Handelskade façade colours instead, so the row reads as the
   waterfront rather than as a component library. The colours are fills, they
   carry no text, so they take the raw measured values in both themes. */

.pillars__h { max-inline-size: 24ch; margin-block-end: clamp(2rem, 3.4vw, 2.75rem); }
.pillars { display: grid; gap: clamp(1.4rem, 2.6vw, 2.25rem); grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1000px) { .pillars { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 540px)  { .pillars { grid-template-columns: 1fr; } }
.pillar { border-block-start: 7px solid var(--kade-cobalt); padding-block-start: 1rem; }
.pillar:nth-child(2) { border-block-start-color: var(--kade-rose); }
.pillar:nth-child(3) { border-block-start-color: var(--kade-green); }
.pillar:nth-child(4) { border-block-start-color: var(--kade-peach); }
.pillar h3 { font-size: var(--f-700); margin-block-end: .5rem; }
.pillar p { color: var(--ink-2); font-size: var(--f-400); line-height: 1.6; }

/* ---------- the cruise block ----------
   Above the grid, deliberately, and it is the only block on the page with a
   photograph beside the text. Their own FAQ is written for this reader more
   than any other, and a cruise passenger decides in the first ten seconds
   whether a tour operator can get them back to the ship. */

.cruise { display: grid; gap: clamp(1.75rem, 4vw, 3rem); grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr); align-items: center; }
@media (max-width: 900px) { .cruise { grid-template-columns: 1fr; } }
.cruise__body h2 { margin-block-end: 1rem; }
/* Capped at the source width: the file is 1264px and nothing here is ever
   upscaled. */
.cruise__pic { margin: 0; }
.cruise__pic img { inline-size: 100%; max-inline-size: 1264px; border-radius: var(--r); }

.gate {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .3rem 1rem;
  margin-block: 1.5rem;
  padding-inline-start: 1.1rem;
  border-inline-start: 7px solid var(--casper-yellow);
}
.gate__big { font-family: var(--display); font-weight: 900; font-size: clamp(1.75rem, 3.4vw, 2.5rem); line-height: 1.05; letter-spacing: -.03em; }
.gate__unit { font-size: var(--f-400); color: var(--muted); }

.cruise__list { list-style: none; margin: 1.25rem 0 0; padding: 0; display: grid; gap: .6rem; font-size: var(--f-400); color: var(--ink-2); }
.cruise__list li { padding-inline-start: 1.6rem; position: relative; line-height: 1.55; }
.cruise__list li::before {
  content: ""; position: absolute; inset-inline-start: 0; inset-block-start: .45em;
  inline-size: 9px; block-size: 9px; border-radius: 50%; background: var(--act);
}
.cruise__body .btn { margin-block-start: 1.75rem; }

/* ---------- the tour grid ----------
   THREE items and the columns are STATED. auto-fit resolving three to two
   leaves an orphan, which is the bug in ../../CLAUDE.md, and three is also
   simply right: each card carries a route and the two lists everybody
   actually reads, and three of those side by side is the whole comparison in
   one screen.

   A card is a fill and a photograph, with one line on it — the one that
   separates the name and price from the route. That line does work. A border
   on all four sides does not. */

.tours { display: grid; gap: 1.15rem; grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 1080px) { .tours { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 760px)  { .tours { grid-template-columns: 1fr; } }

.tour {
  container-type: inline-size;
  background: var(--surface); border-radius: var(--r); overflow: hidden;
  display: flex; flex-direction: column;
  transition: translate .2s ease, box-shadow .2s ease;
}
.tour:hover { translate: 0 -4px; box-shadow: 0 12px 30px rgb(16 26 51 / .12); }
:root[data-theme="dark"] .tour:hover { box-shadow: 0 12px 30px rgb(0 0 0 / .45); }
.tour__pic { margin: 0; }
.tour__pic img { inline-size: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.tour__body { padding: clamp(1.3rem, 2.4vw, 1.7rem); display: flex; flex-direction: column; gap: .95rem; flex: 1; }

.tour__top { display: flex; flex-direction: column; gap: .4rem; padding-block-end: .9rem; border-block-end: 1px solid var(--line); }
.tour h3 { font-size: var(--f-800); line-height: 1.1; }
.tour__price { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem .75rem; }
/* The price, three steps up the scale in the action colour, because price and
   duration are the two things a visitor is here to line up. */
.tour__fig { font-size: var(--f-900); font-weight: 500; line-height: 1.05; color: var(--act); }
/* The one tour whose price their own site contradicts itself about. It gets
   the same slot, at a size the eye can still line up, in words instead of a
   number — never a figure we cannot stand behind. */
.tour__ask { font-family: var(--display); font-weight: 800; font-size: var(--f-700); line-height: 1.15; color: var(--act); letter-spacing: -.02em; }
.tour__dur { font-size: var(--f-300); color: var(--muted); }

/* At card widths over 24rem the name and the price sit on one line; below
   that they stack. A container query, not a media query: the card does not
   know or care how wide the viewport is. */
@container (min-width: 24rem) {
  .tour__top { flex-direction: row; align-items: baseline; justify-content: space-between; gap: 1rem; }
  .tour__price { flex: none; flex-direction: column; align-items: flex-end; gap: .1rem; }
}

.tour__lede { font-size: var(--f-400); color: var(--ink-2); line-height: 1.6; }
.route { margin: 0; padding-inline-start: 1.4rem; display: grid; gap: .5rem; font-size: var(--f-400); color: var(--ink-2); line-height: 1.5; }
.route li::marker { font-family: var(--fig); font-size: var(--f-200); color: var(--muted); }
.route b { color: var(--ink); font-weight: 800; }

.tour__inc { font-size: var(--f-300); color: var(--muted); line-height: 1.55; margin-block-start: auto; padding-block-start: .3rem; }
.tour__inc b { color: var(--ink-2); font-weight: 800; }

.tour__cta { align-self: flex-start; font-size: var(--f-400); font-weight: 800; color: var(--act); text-decoration: none; padding-block: .2rem; border-block-end: 3px solid color-mix(in srgb, var(--act) 35%, transparent); }
.tour__cta:hover { border-block-end-color: var(--act); }

/* ---------- good to know ----------
   Sixty-four question-and-answer pairs exist and roughly a fifth of them
   decide a booking. Those are on the page, as text, in one native <details>
   accordion — a real disclosure element, so the expanded state, the keyboard
   behaviour and the screen-reader announcement come from the browser rather
   than from three ARIA attributes that have to stay in sync. */

.faq { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 clamp(2rem, 4vw, 3.5rem); align-items: start; }
@media (max-width: 860px) { .faq { grid-template-columns: 1fr; } }

.qa { border-block-end: 1px solid var(--line); }
.qa summary {
  cursor: pointer; list-style: none;
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  padding-block: 1rem;
  font-family: var(--display); font-weight: 700; font-size: var(--f-600); line-height: 1.3; letter-spacing: -.018em;
}
.qa summary::-webkit-details-marker { display: none; }
.qa summary::after {
  content: "+"; flex: none;
  font-family: var(--display); font-weight: 700; font-size: var(--f-800); line-height: 1;
  color: var(--act); transition: rotate .25s cubic-bezier(.2,.7,.3,1);
}
.qa[open] summary::after { rotate: 45deg; }
.qa summary:hover { color: var(--act); }
.qa p { padding-block-end: 1.1rem; font-size: var(--f-400); color: var(--ink-2); line-height: 1.65; max-inline-size: 56ch; }

@media (prefers-reduced-motion: no-preference) {
  .qa[open] p { animation: qa-in .24s cubic-bezier(.2,.7,.3,1) both; }
  @keyframes qa-in { from { opacity: 0; translate: 0 -4px; } to { opacity: 1; translate: 0 0; } }
}

/* ---------- who we are ---------- */

.who { display: grid; gap: clamp(1.5rem, 3vw, 2.5rem); grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); align-items: start; }
@media (max-width: 860px) { .who { grid-template-columns: 1fr; } }
.who__lede { font-family: var(--display); font-weight: 700; font-size: clamp(1.4rem, 2.6vw, 1.95rem); line-height: 1.22; letter-spacing: -.028em; }
.who__body p:not(.who__lede) { color: var(--ink-2); line-height: 1.68; margin-block-start: 1.1rem; }

.trust { display: grid; gap: 1.4rem; align-content: start; }
.trust > div { border-inline-start: 5px solid var(--casper-violet); padding-inline-start: 1.05rem; }
.trust > div:nth-child(2) { border-inline-start-color: var(--knip); }
.trust > div:nth-child(3) { border-inline-start-color: var(--casper-yellow); }
.trust h3 { font-size: var(--f-600); margin-block-end: .3rem; }
.trust p { font-size: var(--f-400); color: var(--ink-2); line-height: 1.6; }

/* ---------- fact lists ---------- */

.facts { margin: 0; background: var(--surface); border-radius: var(--r); overflow: hidden; }
.sec--raise .facts, .sec--band .facts { background: var(--ground); }
.facts > div { display: grid; grid-template-columns: minmax(0, 9rem) minmax(0, 1fr); gap: .25rem 1.5rem; padding: .95rem clamp(1.1rem, 2vw, 1.45rem); border-block-end: 1px solid var(--line); }
.facts > div:last-child { border-block-end: 0; }
.facts dt { font-size: var(--f-200); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); font-weight: 800; padding-block-start: .15rem; }
.facts dd { margin: 0; font-size: var(--f-400); color: var(--ink-2); line-height: 1.6; }
.facts a { color: var(--act); font-weight: 800; }
@media (max-width: 620px) { .facts > div { grid-template-columns: 1fr; } }

/* ---------- the request form ---------- */

.contact { display: grid; gap: clamp(1.75rem, 4vw, 3.25rem); grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); align-items: start; }
@media (max-width: 900px) { .contact { grid-template-columns: 1fr; } }

.form { display: grid; gap: .95rem; }
.form__row { display: grid; gap: .95rem; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 560px) { .form__row { grid-template-columns: 1fr; } }

.field { display: flex; flex-direction: column; gap: .3rem; }
.field label { font-size: var(--f-200); font-weight: 800; letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--muted); }
/* The form lives on the yellow band, where --muted is mixed for the page
   ground and goes grey-on-yellow. On the band the labels take the band's own
   themed second colour: #3E3A1B on #EBE136 is 8.41 : 1. */
.sec--band .field label { color: var(--band-2); }
.field .req { color: var(--tile); }
.field input, .field select, .field textarea {
  font-family: var(--body); font-size: var(--f-500); color: var(--ink);
  background: var(--surface); border: 1px solid var(--line-2); border-radius: var(--r-sm);
  padding: .68rem .8rem; inline-size: 100%; min-block-size: 44px;
  transition: border-color .2s ease;
}
.field textarea { min-block-size: 6.5rem; resize: vertical; field-sizing: content; max-block-size: 15rem; }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--muted); }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--act); border-width: 2px; }

/* Two routes to the same state. :user-invalid catches the browser's own
   verdict once a field has been touched and left; .invalid is what the script
   sets after a submit. Neither needs the other. */
.field:has(:user-invalid) input, .field:has(:user-invalid) select, .field:has(:user-invalid) textarea,
.field.invalid input, .field.invalid select, .field.invalid textarea { border-color: var(--warn); }

.field .err { font-size: var(--f-200); color: var(--warn); line-height: 1.4; font-weight: 700; }
/* Without this an empty slot reserves its line-height on every field. On
   Everts that came to 115px of invisible space down one panel. */
.field .err:empty { display: none; }

/* The form sits on the yellow band, so its own small print takes the band's
   themed pair rather than --muted, which is mixed for the page ground and
   goes grey-on-yellow. */
.sec--band .field .err { color: #7A2C0C; }
:root[data-theme="dark"] .sec--band .field .err { color: var(--warn); }

.form__foot { display: flex; flex-wrap: wrap; gap: .85rem 1.25rem; align-items: center; margin-block-start: .3rem; }
.form__foot p { font-size: var(--f-200); color: var(--band-2); max-inline-size: 34ch; line-height: 1.5; }
/* On the yellow band the solid button would be yellow on yellow. It takes the
   navy here, in both themes, and the pair is computed: #25356B carries
   #FFFFFF at 11.67 : 1 and sits on #EBE136 at 8.53 : 1 in light; in dark the
   band is --surface and the button goes back to the yellow pair. */
.sec--band .btn--solid { background: var(--casper-navy); color: #FFFFFF; }
.sec--band .btn--solid:hover { background: #1B2A57; }
:root[data-theme="dark"] .sec--band .btn--solid { background: var(--cta); color: var(--cta-on); }
:root[data-theme="dark"] .sec--band .btn--solid:hover { background: var(--cta-hover); }

.status { border-inline-start: 3px solid var(--line-2); padding: .55rem 0 .55rem 1rem; font-size: var(--f-300); line-height: 1.6; display: none; }
.status.show { display: block; }
.status--ok { border-inline-start-color: var(--good); color: var(--good); }
.status--bad { border-inline-start-color: var(--warn); color: var(--warn); }
.sec--band .status--ok { color: #0C4632; border-inline-start-color: #0C4632; }
.sec--band .status--bad { color: #7A2C0C; border-inline-start-color: #7A2C0C; }
:root[data-theme="dark"] .sec--band .status--ok { color: var(--good); border-inline-start-color: var(--good); }
:root[data-theme="dark"] .sec--band .status--bad { color: var(--warn); border-inline-start-color: var(--warn); }
.status a { color: inherit; font-weight: 800; }

.reach { display: grid; gap: 1.25rem; align-content: start; }
.reach .facts dt { color: var(--muted); }

/* ---------- footer ---------- */

/* The kade stripe again, at the other end of the page. Same gradient, same
   measured façades, no text on it. */
.ftr { position: relative; padding-block: clamp(2.75rem, 5vw, 3.75rem) 1.75rem; }
.ftr::before { content: ""; position: absolute; inset-inline: 0; inset-block-start: 0; block-size: 9px; background: var(--kade); }
.ftr .wrap { display: grid; gap: clamp(1.75rem, 3.5vw, 3rem); grid-template-columns: 2fr 1fr; }
@media (max-width: 620px) { .ftr .wrap { grid-template-columns: 1fr; } }
.ftr .brand__mark { inline-size: 54px; block-size: 54px; }
.ftr__blurb { margin-block-start: .9rem; color: var(--muted); font-size: var(--f-400); max-inline-size: 34ch; line-height: 1.6; }
.ftr__h { font-family: var(--body); font-size: var(--f-200); letter-spacing: var(--ls-label); text-transform: uppercase; font-weight: 800; color: var(--muted); margin: 0 0 .9rem; }
.ftr ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.ftr a { color: var(--ink-2); text-decoration: none; font-size: var(--f-400); }
.ftr a:hover { color: var(--act); }
.ftr__legal { grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: .5rem 2rem; padding-block-start: 1.75rem; margin-block-start: .75rem; border-block-start: 1px solid var(--line); font-size: var(--f-200); color: var(--muted); }

/* ---------- 404 ---------- */
.notfound { display: grid; place-items: center; min-block-size: 58svh; }
.notfound > div { max-inline-size: 46ch; }
.notfound .label { color: var(--act); }
.notfound .lede { margin-block-start: 1.05rem; }
.notfound .hero__cta { display: flex; gap: .6rem; flex-wrap: wrap; margin-block-start: 1.75rem; }

/* ---------- chat ----------
   An assistant that answers from what this business has published, and a
   button that hands the whole conversation over the moment it cannot. Front
   end here; the model, the guard rails and the knowledge base are in
   functions/api/chat.js and functions/_kb.js.

   SHARED COMPONENT. Everything below the token map is byte-identical on every
   site in workshop/ — only the map is per-site, and it only ever maps this
   site's own tokens onto the --chat-* names. If a change is needed, change it
   in all of them, or it stops being shared.

   The panel is non-modal on purpose: it opens beside the page rather than
   over it, so nothing behind it is blocked and no focus trap is needed.
   Escape closes it, a click outside closes it, and focus returns to the
   launcher. */

:root {
  /* --act, NOT --cta. The shared block below puts --chat-accent on as TEXT
     (.chat__handoff, .chat__call) against --chat-surface, and their logo
     yellow on white is 1.24 : 1. See the note in the file header. */
  --chat-accent:    var(--act);
  --chat-accent-on: var(--act-on);
  --chat-surface:   var(--surface);
  --chat-raise:     var(--surface-2);
  --chat-ink:       var(--ink);
  --chat-ink-2:     var(--ink-2);
  --chat-muted:     var(--muted);
  --chat-line:      var(--line);
  --chat-line-soft: var(--line);
  --chat-warn:      var(--warn);
  --chat-display:   var(--display);
  --chat-body:      var(--body);
  --chat-r: 16px;
  --chat-r-sm: 5px;
  --chat-r-pill: 999px;
}

.chat-launch {
  position: fixed; z-index: 70;
  right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem);
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .8rem 1.25rem .8rem 1rem;
  background: var(--chat-accent); color: var(--chat-accent-on);
  border: 0; border-radius: var(--chat-r-pill); cursor: pointer;
  box-shadow: 0 4px 12px rgb(0 0 0 / .18), 0 18px 44px rgb(0 0 0 / .22);
  font-family: var(--chat-body); font-weight: 600; font-size: var(--f-400);
  letter-spacing: var(--ls-caps, .04em); line-height: 1;
  transition: filter .22s ease, translate .22s cubic-bezier(.2,.7,.3,1), box-shadow .22s ease;
}
.chat-launch:hover { filter: brightness(1.08); translate: 0 -2px; box-shadow: 0 6px 16px rgb(0 0 0 / .22), 0 26px 60px rgb(0 0 0 / .28); }
.chat-launch svg { width: 22px; height: 22px; flex: none; }

/* Arrives with one small rise so the eye catches it, then never moves again. */
@media (prefers-reduced-motion: no-preference) {
  .chat-launch { animation: launch-in .5s cubic-bezier(.2,.7,.3,1) 1.1s both; }
  @keyframes launch-in { from { opacity: 0; translate: 0 14px; } to { opacity: 1; translate: 0 0; } }
}

.chat-launch[aria-expanded="true"] .chat-launch__open,
.chat-launch[aria-expanded="false"] .chat-launch__close { display: none; }
.chat-launch[aria-expanded="true"] .chat-launch__label { display: none; }
.chat-launch[aria-expanded="true"] { padding: .8rem 1rem; }

@media (max-width: 560px) {
  .chat-launch__label { display: none; }
  .chat-launch { padding: .8rem .95rem; }
}

/* ---- the panel ----
   A conversation, so it needs a fixed frame with the log scrolling inside it.
   Left to grow with its content a chat panel walks off the top of the screen
   after four exchanges.

   Rows: head / log / composer / handoff / status / disclosure. The log is the
   only one that flexes, and minmax(0,1fr) is what actually lets it scroll —
   1fr alone takes its minimum from the content and the panel overflows. */

.chat {
  position: fixed; z-index: 71;
  right: clamp(1rem, 3vw, 2rem);
  bottom: calc(clamp(1rem, 3vw, 2rem) + 66px);
  width: min(24rem, calc(100vw - 2rem));
  height: min(34rem, calc(100svh - clamp(1rem, 3vw, 2rem) - 94px));
  background: var(--chat-surface); color: var(--chat-ink);
  border: 1px solid var(--chat-line); border-radius: var(--chat-r);
  box-shadow: 0 8px 22px rgb(0 0 0 / .14), 0 32px 80px rgb(0 0 0 / .22);
  overflow: hidden;
  display: none; grid-template-rows: auto minmax(0, 1fr) auto auto auto auto;
  opacity: 0; translate: 0 12px; scale: .98;
  transform-origin: bottom right;
  transition: opacity .26s ease, translate .26s cubic-bezier(.2,.7,.3,1),
              scale .26s cubic-bezier(.2,.7,.3,1), display .26s allow-discrete;
}
.chat.is-open { display: grid; opacity: 1; translate: 0 0; scale: 1; }
@starting-style { .chat.is-open { opacity: 0; translate: 0 12px; scale: .98; } }

.chat__head { padding: 1rem 1.15rem .8rem; border-bottom: 1px solid var(--chat-line-soft); }
.chat__title { font-family: var(--chat-display); font-size: var(--f-700); line-height: 1.15; margin: 0 0 .25rem; }
.chat__intro { font-size: var(--f-200); color: var(--chat-muted); margin: 0; line-height: 1.5; }

/* ---- the log ---- */

.chat__log {
  overflow-y: auto; overscroll-behavior: contain;
  padding: .95rem 1.15rem;
  display: flex; flex-direction: column; gap: .5rem;
  scrollbar-width: thin; scrollbar-color: var(--chat-line) transparent;
}

.msg {
  max-width: 88%;
  padding: .55rem .8rem;
  border-radius: var(--chat-r);
  font-size: var(--f-400); line-height: 1.55;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.msg--bot { align-self: flex-start; background: var(--chat-raise); border-bottom-left-radius: var(--chat-r-sm); }
.msg--me  { align-self: flex-end; background: var(--chat-accent); color: var(--chat-accent-on); border-bottom-right-radius: var(--chat-r-sm); }

.msg--note {
  align-self: stretch; max-width: none; text-align: center;
  background: none; padding: .15rem 0;
  font-size: var(--f-100); color: var(--chat-muted); line-height: 1.5;
}

/* Each turn arrives with one small rise. Reduced motion gets it instantly. */
@media (prefers-reduced-motion: no-preference) {
  .msg { animation: msg-in .26s cubic-bezier(.2,.7,.3,1) both; }
  @keyframes msg-in { from { opacity: 0; translate: 0 6px; } to { opacity: 1; translate: 0 0; } }
}

.msg--typing { display: inline-flex; gap: .3rem; align-items: center; padding: .75rem .8rem; }
.msg--typing i { width: 6px; height: 6px; border-radius: 50%; background: var(--chat-muted); display: block; }
@media (prefers-reduced-motion: no-preference) {
  .msg--typing i { animation: blip 1.1s ease-in-out infinite; }
  .msg--typing i:nth-child(2) { animation-delay: .16s; }
  .msg--typing i:nth-child(3) { animation-delay: .32s; }
  @keyframes blip { 0%, 60%, 100% { opacity: .3; translate: 0 0; } 30% { opacity: 1; translate: 0 -3px; } }
}

/* ---- the composer ---- */

.chat__composer {
  display: flex; gap: .55rem; align-items: end;
  padding: .65rem 1.15rem;
  border-top: 1px solid var(--chat-line-soft);
}
.chat__composer textarea {
  flex: 1;
  font-family: var(--chat-body); font-size: var(--f-400); color: var(--chat-ink);
  background: transparent; border: 0; border-bottom: 1px solid var(--chat-line);
  border-radius: 0; padding: .45rem 0; resize: none;
  min-height: 2.3rem; max-height: 6.5rem; field-sizing: content;
  transition: border-color .25s ease;
}
.chat__composer textarea:hover { border-bottom-color: var(--chat-muted); }
.chat__composer textarea:focus { outline: none; border-bottom-color: var(--chat-accent); border-bottom-width: 2px; }

.chat__send {
  flex: none; width: 36px; height: 36px; margin-bottom: .15rem;
  display: grid; place-items: center;
  background: var(--chat-accent); color: var(--chat-accent-on);
  border: 0; border-radius: 50%; cursor: pointer;
  transition: filter .2s ease, opacity .2s ease;
}
.chat__send svg { width: 17px; height: 17px; }
.chat__send:hover { filter: brightness(1.08); }
.chat__send:disabled { opacity: .35; cursor: default; }

/* ---- handing over ---- */

.chat__foot {
  display: flex; align-items: center; gap: .4rem .75rem; flex-wrap: wrap;
  padding: 0 1.15rem .8rem;
}
.chat__handoff {
  font-family: var(--chat-body); font-size: var(--f-200); font-weight: 600;
  letter-spacing: .02em;
  background: transparent; color: var(--chat-accent);
  border: 1px solid color-mix(in oklab, var(--chat-accent) 45%, transparent);
  border-radius: var(--chat-r-pill); padding: .38rem .8rem; cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
.chat__handoff:hover { background: color-mix(in oklab, var(--chat-accent) 12%, transparent); border-color: var(--chat-accent); }
.chat__handoff:disabled { opacity: .45; cursor: default; }
.chat__call { font-size: var(--f-100); color: var(--chat-muted); }
.chat__call:hover { color: var(--chat-ink); }

.chat__ho { padding: 0 1.15rem .8rem; border-top: 1px solid var(--chat-line-soft); }
.chat__ho[hidden] { display: none; }
.chat__ho-note { font-size: var(--f-200); color: var(--chat-ink-2); margin: .8rem 0 .55rem; line-height: 1.5; }

.chat__field { display: flex; flex-direction: column; gap: .25rem; margin-bottom: .55rem; }
.chat__field label {
  font-family: var(--chat-body); font-size: var(--f-100); font-weight: 600;
  letter-spacing: var(--ls-label, .1em); text-transform: uppercase; color: var(--chat-muted);
}
.chat__field input, .chat__field textarea {
  font-family: var(--chat-body); font-size: var(--f-400); color: var(--chat-ink);
  background: transparent; border: 0; border-bottom: 1px solid var(--chat-line);
  border-radius: 0; padding: .4rem 0; width: 100%; resize: none;
  transition: border-color .25s ease;
}
.chat__field textarea { min-height: 2.6rem; max-height: 6rem; field-sizing: content; }
.chat__field input:focus, .chat__field textarea:focus { outline: none; border-bottom-color: var(--chat-accent); border-bottom-width: 2px; }
.chat__field.invalid input, .chat__field.invalid textarea { border-bottom-color: var(--chat-warn); }
.chat__err { font-size: var(--f-100); color: var(--chat-warn); line-height: 1.4; }
.chat__err:empty { display: none; }

.chat__submit {
  width: 100%; margin-top: .2rem;
  font-family: var(--chat-body); font-size: var(--f-300); font-weight: 600;
  letter-spacing: var(--ls-caps, .04em);
  background: var(--chat-accent); color: var(--chat-accent-on);
  border: 0; border-radius: var(--chat-r-pill); padding: .6rem 1rem; cursor: pointer;
  transition: filter .2s ease;
}
.chat__submit:hover { filter: brightness(1.08); }
.chat__submit:disabled { opacity: .5; cursor: default; }

.chat__status {
  margin: 0 1.15rem .8rem; padding: .45rem 0 .45rem .85rem;
  border-left: 2px solid var(--chat-warn);
  font-size: var(--f-200); line-height: 1.55; color: var(--chat-warn);
  display: none;
}
.chat__status.show { display: block; }
.chat__status a { color: inherit; font-weight: 600; }

/* The one honest thing a chatbot owes anybody. */
.chat__disclosure {
  padding: 0 1.15rem .75rem; margin: 0;
  font-size: var(--f-100); color: var(--chat-muted); line-height: 1.5;
}

/* For labels a screen reader needs and the eye does not. */
.chat-vh { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

@media (max-width: 560px) {
  .chat {
    left: 1rem; right: 1rem; width: auto;
    bottom: calc(1rem + 62px);
    height: min(30rem, calc(100svh - 1rem - 86px));
  }
}
