/* ============================================================================
   Visa Calculator, structural stylesheet
   Identical across all three directions. Every visual decision routes through a
   custom property defined in tokens-*.css, so the three prototypes prove the
   IA is shared and only the skin differs.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: 1.6;
  font-feature-settings: var(--font-features, normal);
}
h1, h2, h3, p, ul, ol, figure { margin: 0; }
ul { list-style: none; padding: 0; }
button { font: inherit; color: inherit; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 3px; }

.wrap { max-width: var(--measure); margin: 0 auto; padding: 0 var(--gutter); }

/* every anchor the header links to has to clear the header when jumped to,
   or the section title lands underneath it. */
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
#pick-dates, #how-we-count, #answer, #rule, #says, #limits, #why { scroll-margin-top: 82px; }
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ---------- ② headline -------------------------------------------------- */
.hero { padding-block: var(--s7) var(--s6); }
.hero h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: 1.05;
  letter-spacing: var(--track-h1);
  font-weight: var(--weight-display);
  max-width: 18ch;
}
.hero p { margin-top: var(--s4); font-size: var(--fs-lead); color: var(--ink-2); max-width: 58ch; }
.hero p b { color: var(--ink); font-weight: 600; }

/* ---------- first screen: named, actionable, compared -------------------
   Added 2026-08-06 for the audit's first-screen and primary-action findings.
   The H1 keeps its plain question; everything the audit said was missing,
   Schengen, the audience, the tool word, the privacy mechanism, a real
   button, lands directly beneath it in one block. */
.hero__sub { font-size: var(--fs-lead); color: var(--ink); max-width: 60ch; }
.hero__actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s3) var(--s4); margin-top: var(--s5);
}
.btn--lg { min-height: 52px; padding-inline: var(--s6); font-size: var(--fs-base); }
.hero__why {
  margin-top: var(--s5); max-width: 62ch;
  display: grid; gap: var(--s3); list-style: none; padding: 0;
}
.hero__why li {
  font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.5;
  padding-left: var(--s5); position: relative;
}
.hero__why li::before {
  content: "\2713"; position: absolute; left: 0; top: 0;
  color: var(--ok); font-weight: 700;
}
.hero__why b { color: var(--ink); font-weight: 600; }
.hero__why a { color: var(--link); }

/* ---------- the two objections, answered beside the button --------------
   Replaces the full-width status bar that used to sit above the masthead.
   Only two of its four claims survive here, and neither is a chip: "what will
   this cost me" and "is this current" are the questions that stop someone
   starting, so each gets a sentence. Four grey phrases in one 12px line
   answered neither. */
.hero__assure {
  display: grid; gap: var(--s3);
  margin-top: var(--s5); max-width: 62ch;
  padding: 0; list-style: none;
}
@media (min-width: 720px) { .hero__assure { grid-template-columns: 1fr 1fr; gap: var(--s4); } }
.assure {
  display: flex; gap: var(--s3);
  padding: var(--s4);
  background: var(--surface);
  border: var(--hairline); border-radius: var(--radius);
}
.assure__ic { width: 20px; height: 20px; flex: none; margin-top: 1px; color: var(--ok); }
.assure__t { font-size: var(--fs-xs); line-height: 1.5; color: var(--ink-2); }
.assure__t b { display: block; margin-bottom: 3px; font-size: var(--fs-sm); font-weight: 650; color: var(--ink); }
.assure__t em { font-style: normal; font-weight: 600; color: var(--ink); }

/* ---------- ① site header ----------------------------------------------
   Sticky at the top. It does not fight the answer bar, which is fixed to the
   bottom of the viewport, the two occupy opposite edges by design.
   Brand says what the site is, the nav reaches the four things worth reading,
   and the action that starts the tool stays reachable at any scroll depth. */
.topbar {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  border-bottom: var(--hairline);
  backdrop-filter: blur(12px) saturate(1.4);
  -webkit-backdrop-filter: blur(12px) saturate(1.4);
}
@supports not (backdrop-filter: blur(1px)) { .topbar { background: var(--bg); } }
.topbar__in {
  display: flex; align-items: center; gap: var(--s5);
  min-height: 66px; padding-block: var(--s3);
}

/* brand ------------------------------------------------------------------ */
.brand {
  display: inline-flex; align-items: center; gap: 11px;
  margin-right: auto; text-decoration: none; color: inherit;
  border-radius: var(--radius-sm);
}
.brand__mark {
  display: grid; place-items: center; flex: none;
  width: 34px; height: 34px; border-radius: 10px;
  background: var(--accent); color: var(--accent-ink);
}
.brand__mark svg { width: 19px; height: 19px; }
.brand__t { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.brand__n {
  font-family: var(--font-display); font-size: 16px; font-weight: 640;
  letter-spacing: -.017em; color: var(--ink); line-height: 1.15; white-space: nowrap;
}
.brand__n b { font-weight: 640; color: var(--ok); }
.brand__sub {
  font-size: 11.5px; font-weight: 500; letter-spacing: .012em;
  color: var(--ink-3); line-height: 1.2; white-space: nowrap;
}
.brand:hover .brand__n { color: var(--ok); }

/* section links ---------------------------------------------------------- */
.topnav { display: flex; align-items: center; gap: 2px; }
.topnav a {
  position: relative;
  display: inline-flex; align-items: center; min-height: 38px;
  padding: 0 11px; border-radius: var(--radius-sm);
  font-size: var(--fs-sm); font-weight: 550; color: var(--ink-2);
  text-decoration: none; white-space: nowrap;
  transition: color .16s var(--ease), background-color .16s var(--ease);
}
.topnav a::after {
  content: ""; position: absolute; left: 11px; right: 11px; bottom: 6px;
  height: 1.5px; border-radius: 2px; background: var(--ok);
  transform: scaleX(0); transform-origin: left;
  transition: transform .2s var(--ease);
}
.topnav a:hover { color: var(--ink); background: var(--legend-bg); }
.topnav a:hover::after { transform: scaleX(1); }

/* The two controls at the right of the masthead are a pair, so they get one
   explicit height rather than three different intrinsic ones. */
.topbar__cta { flex: none; height: 44px; min-height: 0; padding-block: 0; padding-inline: var(--s5); font-size: var(--fs-sm); }

/* My trips: outlined rather than plain like the section links, because it is a
   control for the visitor's own data and not another thing to read. It is the
   only nav element that survives to phone width. */
.navtrips {
  display: inline-flex; align-items: center; gap: 8px; flex: none;
  height: 44px; padding: 0 var(--s4); border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 var(--border-w) var(--line);
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink);
  text-decoration: none; white-space: nowrap;
  transition: box-shadow .16s var(--ease), background-color .16s var(--ease);
}
.navtrips__ic { width: 17px; height: 17px; flex: none; color: var(--ink-2); }
.navtrips__c {
  display: inline-grid; place-items: center; min-width: 20px; height: 20px;
  padding: 0 5px; border-radius: 99px;
  background: var(--ok); color: var(--accent-ink);
  font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1;
}
body.has-breach .navtrips__c { background: var(--bad); }
@media (hover: hover) and (pointer: fine) {
  .navtrips:hover { background: var(--legend-bg); box-shadow: inset 0 0 0 var(--border-w) var(--ink-3); }
}
/* the label goes before the section links do: the icon and the count carry it */
@media (max-width: 1120px) { .navtrips__t { display: none; } .navtrips { padding-inline: 12px; } }

/* Below 560px the action drops its label too. Brand, My trips and a full-width
   dark button do not fit on a 360px row, and the button is the one that can
   lose its words without losing its meaning. */
@media (max-width: 560px) {
  .topbar__cta > span { display: none; }
  .topbar__cta { width: 44px; min-width: 44px; padding-inline: 0; gap: 0; }
  .topbar__cta .ic { width: 20px; height: 20px; }
}

/* Below the point where the four links stop fitting, the brand and the action
   are what matter, the page is a single scroll and the bottom answer bar
   already handles wayfinding, so a hamburger would add a control to hide
   four anchors nobody is hunting for on a phone. */
/* The section links go at 1080px, not 900: My trips takes the space they used
   to have, and below this the CTA was pushed past the right edge. */
@media (max-width: 1080px) { .topnav { display: none; } }
@media (max-width: 460px) {
  .brand__sub { display: none; }
  .topbar__in { gap: var(--s2); min-height: 58px; }
  /* No padding-inline override here: it used to re-inflate the action after the
     560px rule had squared it off, so the button stayed 48px and pushed itself
     past the right edge at 360. */
}

/* ---------- ⑬a why professionals recommend this -------------------------
   A numbered argument rather than a card grid, so it never reads as a second
   copy of the four cards below it. This section answers "should I use a
   calendar tool at all"; that one answers "why is this one built like this". */
.pro { padding-block: var(--s7); border-top: var(--hairline); }
.pro h2 {
  font-size: var(--fs-h2); font-family: var(--font-display);
  letter-spacing: var(--track-h2); font-weight: var(--weight-display); max-width: 24ch;
}
.pro__lede {
  margin-top: var(--s4); max-width: 68ch;
  font-size: var(--fs-lead); color: var(--ink-2); line-height: 1.6; text-wrap: pretty;
}
.pro__lede b { color: var(--ink); font-weight: 600; }

.pro__list { counter-reset: pro; margin-top: var(--s6); display: grid; gap: var(--s5); max-width: 76ch; padding: 0; list-style: none; }
.pro__list li { counter-increment: pro; position: relative; padding-left: 52px; }
.pro__list li::before {
  content: counter(pro);
  position: absolute; left: 0; top: 0;
  width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--legend-bg); color: var(--ink);
  font-family: var(--font-numeral); font-size: var(--fs-sm); font-weight: 700;
  box-shadow: inset 0 0 0 1px var(--line);
}
.pro__list h3 {
  font-family: var(--font-display); font-size: var(--fs-lead);
  font-weight: 650; letter-spacing: -.012em; line-height: 1.3; color: var(--ink);
  padding-top: 4px;
}
.pro__list p { margin-top: var(--s2); font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6; text-wrap: pretty; }
.pro__list b { color: var(--ink); font-weight: 600; }

.pro__note {
  margin-top: var(--s6); max-width: 76ch;
  padding: var(--s4) var(--s5); border-radius: var(--radius);
  background: var(--legend-bg);
  font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6;
}
.pro__note b { color: var(--ink); font-weight: 650; }
.pro__note a { color: var(--link); font-weight: 600; }

/* ---------- ⑬ why this tool --------------------------------------------
   Sits between the tool and the testimonials. Cards rather than prose because
   the four reasons are independent, nobody reads this top to bottom, they
   scan it for the one that answers their doubt. The source line inside each
   card is set apart from the claim above it, so the reasoning and the evidence
   never read as one undifferentiated block of confidence. */
.why { padding-block: var(--s7); border-top: var(--hairline); }
.why__head { max-width: 64ch; }
.why h2 {
  font-size: var(--fs-h2); font-family: var(--font-display);
  letter-spacing: var(--track-h2); font-weight: var(--weight-display);
}
.why__lede { margin-top: var(--s3); font-size: var(--fs-lead); color: var(--ink-2); line-height: 1.55; text-wrap: pretty; }

.why__grid { display: grid; gap: var(--s4); margin-top: var(--s5); grid-template-columns: 1fr; }
@media (min-width: 760px) { .why__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s5) var(--s4); } }

.why__card {
  display: flex; flex-direction: column;
  padding: var(--s5); background: var(--surface);
  border: var(--hairline); border-radius: var(--radius);
}
.why__ic {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: 11px; margin-bottom: var(--s4);
  background: var(--day-in-bg); color: var(--ok);
}
.why__ic svg { width: 21px; height: 21px; }
.why__card h3 {
  font-family: var(--font-display); font-size: var(--fs-lead);
  font-weight: 650; letter-spacing: -.012em; line-height: 1.25; color: var(--ink);
}
.why__card p { margin-top: var(--s3); font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6; text-wrap: pretty; }
.why__card b { color: var(--ink); font-weight: 650; }
.why__card em { font-style: normal; font-weight: 600; color: var(--ink); }
/* the evidence, held apart from the claim */
.why__src {
  margin-top: var(--s4) !important; padding-top: var(--s3);
  border-top: var(--hairline); font-size: var(--fs-xs) !important; color: var(--ink-3) !important;
}
.why__src b { color: var(--ink-2); }

.why__note {
  margin-top: var(--s5); max-width: 78ch;
  padding: var(--s4) var(--s5); border-left: 3px solid var(--line);
  font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.6;
}
.why__note b { color: var(--ink); font-weight: 650; }
.why__note em { font-style: normal; font-weight: 600; color: var(--ink); }

/* ---------- what people say -------------------------------------------
   Real comments, quoted verbatim, sitting low on the page, after the answer
   and after the limits. Restrained on purpose: no stars, no stock headshots,
   no carousel, no invented "1M+ users". On a tool people trust with a
   compliance question, overstated proof costs more trust than it earns.
   The card does the work: paper surface on the page's warm ground, one
   hairline, a quiet quote mark, and the attribution given its own rule. */
.says { padding-block: var(--s7); }
.says__head { max-width: 62ch; }
.says h2 {
  font-size: var(--fs-h2); font-family: var(--font-display);
  letter-spacing: var(--track-h2); font-weight: var(--weight-display);
}
.says__lede { margin-top: var(--s3); font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.55; }

.says__grid {
  display: grid; gap: var(--s4); margin-top: var(--s5);
  grid-template-columns: 1fr;
}
@media (min-width: 620px)  { .says__grid { grid-template-columns: repeat(2, 1fr); gap: var(--s5) var(--s4); } }
@media (min-width: 1000px) { .says__grid { grid-template-columns: repeat(4, 1fr); } }

.says__item {
  position: relative; margin: 0;
  display: flex; flex-direction: column;
  padding: var(--s5) var(--s4) var(--s4);
  background: var(--surface);
  border: var(--hairline); border-radius: var(--radius);
  transition: border-color .18s var(--ease), background-color .18s var(--ease);
}
/* the quote mark is decoration, not content, it must never reach a screen
   reader, and it must not push the text it decorates out of alignment. */
.says__item::before {
  content: "\201C";
  position: absolute; top: -2px; left: 12px;
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 54px; line-height: 1; color: var(--line);
  pointer-events: none;
}
.says__item:hover { border-color: var(--ink-3); background: var(--field-bg); }

.says__item blockquote {
  position: relative; margin: 0; flex: 1 1 auto;
  font-size: var(--fs-sm); line-height: 1.6; color: var(--ink);
  text-wrap: pretty;
}

.says__item figcaption {
  display: flex; align-items: center; gap: var(--s3);
  margin-top: var(--s4); padding-top: var(--s3);
  border-top: var(--hairline);
}
.says__who {
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink-2);
  letter-spacing: .005em; overflow-wrap: anywhere;
}

/* ---------- what the tool can't do ------------------------------------
   The audit's anxiety and guarantee findings: the page gave an answer and
   never said what it did not know. Bulleted on purpose, the audit also
   found no scannable list anywhere on the page. */
.limits { padding-block: var(--s7); }
.limits h2 { font-size: var(--fs-h2); font-family: var(--font-display); letter-spacing: var(--track-h2); }
.limits__list { margin-top: var(--s4); max-width: 66ch; display: grid; gap: var(--s3); padding-left: var(--s5); }
.limits__list li { font-size: var(--fs-base); color: var(--ink-2); line-height: 1.55; }
.limits__list b { color: var(--ink); font-weight: 600; }
/* Nothing sets a colour on a bare anchor anywhere in this stylesheet, so a
   link dropped into one of these list items renders in browser blue. Same
   treatment as the one in .limits__keep below it. */
.limits__list a { color: var(--link); font-weight: 600; }
.limits__keep {
  margin-top: var(--s5); max-width: 66ch; font-size: var(--fs-sm); color: var(--ink-2);
  border-left: 3px solid var(--line); padding-left: var(--s4); line-height: 1.55;
}
.limits__keep b { color: var(--ink); }
.limits__keep a { color: var(--link); font-weight: 600; }

/* ---------- ③ the answer ------------------------------------------------
   A panel, not a run of loose elements. It used to start mid-page directly
   under step 3 of the instructions, with no heading and no edge, so the three
   cards read as part of the how-to. The tray is the page's warm grey, which
   also makes the white stat cards sit ON something instead of floating. */
.answer { padding-block: var(--s5) var(--s6); }
.answer__panel {
  padding: var(--s5);
  background: var(--legend-bg);
  border: var(--hairline);
  border-radius: var(--radius);
}
.answer__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s3) var(--s4); flex-wrap: wrap; margin-bottom: var(--s5);
}
.answer__head h2 {
  font-family: var(--font-display); font-size: var(--fs-h2);
  letter-spacing: var(--track-h2); font-weight: var(--weight-display); line-height: 1.15;
}
.answer__sub { margin-top: 5px; font-size: var(--fs-sm); color: var(--ink-2); max-width: 52ch; line-height: 1.5; }
/* the verdict in one word, at the top, where a dashboard states its state */
.answer__status {
  flex: none; align-self: center;
  padding: 7px 13px; border-radius: 99px;
  font-size: var(--fs-xs); font-weight: 700; white-space: nowrap;
  background: var(--surface); color: var(--ink-2);
  box-shadow: inset 0 0 0 1px var(--line);
}
.answer__status[data-state="ok"]    { background: var(--day-in-bg); color: var(--day-in-ink); box-shadow: inset 0 0 0 1px var(--day-in-border); }
.answer__status[data-state="warn"]  { background: var(--note-bg);   color: var(--note-ink);  box-shadow: inset 0 0 0 1px var(--note-border); }
.answer__status[data-state="over"]  { background: var(--bad);       color: #fff;             box-shadow: none; }

.answer__cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s3); }
.stat {
  background: var(--surface); border: var(--hairline); border-radius: var(--radius);
  padding: var(--s5) var(--s4); box-shadow: var(--shadow);
  display: flex; flex-direction: column; gap: var(--s2);
}
.stat--hero { border-color: var(--stat-hero-border); background: var(--stat-hero-bg); }
.stat__v {
  font-family: var(--font-numeral);
  font-size: var(--fs-stat); line-height: .95; font-weight: var(--weight-numeral);
  letter-spacing: var(--track-numeral); font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.stat--hero .stat__v { color: var(--ok); font-size: var(--fs-stat-hero); }
body.is-overstay .stat--hero .stat__v { color: var(--bad); }
/* Empty state: a 76px em-dash reads as a loading bar, not a placeholder.
   Drop it to label size and mute it so the card reads "nothing here yet". */
body.is-empty .stat__v,
body.is-empty .stat--hero .stat__v { color: var(--ink-3); font-size: var(--fs-h2); opacity: .55; }
.stat__k { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .09em; color: var(--ink-2); font-weight: 600; }

.bar { height: 10px; border-radius: 99px; background: var(--bar-track); margin-top: var(--s5); overflow: hidden; }
.bar__fill { height: 100%; border-radius: 99px; background: var(--ok); transition: width .45s var(--ease); }
.bar__fill[data-state="warn"] { background: var(--warn); }
.bar__fill[data-state="over"] { background: var(--bad); }

.verdict { margin-top: var(--s3); font-size: var(--fs-lead); font-weight: 500; }
.answer__return { margin-top: var(--s3); font-size: var(--fs-sm); color: var(--ink-2); }
.answer__return b { color: var(--ink); font-weight: 600; }
.note {
  margin-top: var(--s4); padding: var(--s3) var(--s4); border-radius: var(--radius-sm);
  background: var(--note-bg); border: 1px solid var(--note-border);
  font-size: var(--fs-sm); color: var(--note-ink);
}

/* ---------- breach alert -----------------------------------------------
   The page used to compute its answer at today's date and stop, so a trip
   already entered could run past the limit with nothing on screen saying so.
   This is the loudest thing in the design after the hero number, because an
   entered overstay is the single most expensive mistake this tool exists to
   prevent. Red ground, red hairline, and it names dates rather than telling
   someone they have "an error".

   The 4px rule down the left edge is gone: the ground, the icon and the red
   type already carry the state three times over, and the bar was the one part
   of it that said nothing the other three did not. */
.alert {
  display: flex; gap: var(--s4); align-items: flex-start;
  margin-top: var(--s4); padding: var(--s4) var(--s5);
  background: var(--day-over-bg);
  border: 1px solid var(--bad);
  border-radius: var(--radius-sm); color: var(--day-over-ink);
}
/* the icon is its own column beside the text block, aligned to the cap height
   of the headline rather than to the box */
.alert__ic { width: 22px; height: 22px; flex: none; margin-top: 1px; color: var(--bad); }
.alert__b { min-width: 0; flex: 1; }

/* ---- one container, structure from spacing and type ---------------------
   This was a red box holding a headline pill holding a bordered row: three
   tinted containers deep, and the innermost border measured 1.4:1 against the
   ground behind it, decoration doing an impression of structure. Nesting also
   spends the red three times, which is how a danger colour stops reading as
   one. Everything below is flat now: no inner borders, no inner fills, and
   the hierarchy is carried by size, weight and space.

   All text takes --day-over-ink, the darkest stop of this tint's own ramp,
   at 6.6:1. Nothing here is dimmed with opacity: on a tinted ground that does
   not produce a quieter version of the colour, it produces an unpredictable
   one, and it was being spent on the only lines that say what to do. */
.alert__t {
  font-size: var(--fs-lead); font-weight: 700; line-height: 1.3;
  color: var(--day-over-ink);
}
.alert__body { margin-top: var(--s2); }
.alert__why { font-size: var(--fs-sm); line-height: 1.55; color: var(--day-over-ink); }
.alert__why b { font-weight: 700; }

/* ---- the actions --------------------------------------------------------
   The recovery was an inline run of text at the end of a sentence, which is
   the least prominent form available for the most important thing in the
   component. It is a button in the tint's own palette now: an action inside a
   danger surface belongs to that surface, and a link in some other hue would
   read as navigation that wandered in. One filled button, and only when there
   is exactly one trip to fix, so the fill still means "this is the thing to
   press". */
.alert__acts { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s4); }
.alert__btn {
  min-height: 44px; padding: 0 var(--s5); border-radius: var(--radius-sm);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 650;
  cursor: pointer; white-space: nowrap;
}
.alert__btn--go { border: 0; background: var(--bad); color: #fff; }              /* 6.1:1 */
.alert__btn--quiet {
  background: transparent; color: var(--day-over-ink);
  border: 1.5px solid var(--bad);                                                /* 4.8:1 */
}
.alert__btn--go:hover    { background: var(--day-over-ink); }
.alert__btn--quiet:hover { background: color-mix(in srgb, var(--bad) 12%, transparent); }

/* several breaches: same shape for each, separated by space rather than by
   another set of boxes */
.alert__item { margin-top: var(--s4); }
.alert__item-t {
  font-size: var(--fs-sm); font-weight: 700; color: var(--day-over-ink);
  margin-bottom: 2px;
}
.alert__item .alert__btn { margin-top: var(--s3); }

/* the flagged-row styling lives with the other .trip rules further down, or
   .trip's own background and border (declared later) win */
.trip__over {
  flex: 1 0 100%; display: flex; align-items: center; gap: .5em;
  font-size: var(--fs-sm); font-weight: 600; color: var(--bad);
}
.trip__over svg { width: 16px; height: 16px; flex: none; }

/* the sticky bar's copy of the flag, it replaces the stats rather than
   sitting beside them, because three green numbers next to a red warning
   reads as "mostly fine" */
.sticky__alert {
  display: inline-flex; align-items: center; gap: .5em; flex: none;
  min-height: 40px; padding: 0 var(--s4); border-radius: var(--radius-sm);
  background: var(--bad); color: #fff; text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700;
}
.sticky__alert svg { width: 17px; height: 17px; flex: none; }
.sticky__alert span { opacity: 1; font-weight: 700; }
body.has-breach .sticky__stat,
body.has-breach .sticky__prompt { display: none; }

.actions { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s5); }
.btn {
  border: 0; border-radius: var(--radius-sm); padding: var(--s3) var(--s5);
  min-height: 44px; font-weight: 600; cursor: pointer;
  background: var(--btn-bg); color: var(--btn-ink);
  transition: transform .12s var(--ease), filter .12s var(--ease);
}
.btn:hover { filter: brightness(1.08); }
.btn[disabled] { opacity: .7; cursor: default; }
.btn:active { transform: translateY(1px); }
.btn--ghost { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 var(--border-w) var(--line); }
.btn--text { background: transparent; color: var(--link); padding-inline: var(--s2); min-height: 32px; }

/* ---------- ④ offer ----------------------------------------------------- */
.offer {
  margin-top: var(--s5); padding: var(--s5); border-radius: var(--radius);
  background: var(--offer-bg); border: var(--hairline);
}
.offer__label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .09em; color: var(--ink-3); }
.offer__copy { font-size: var(--fs-lead); font-weight: 600; margin-top: var(--s2); }
.offer__row { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s4); }
.pill {
  display: inline-flex; align-items: center; gap: .5em; min-height: 44px;
  padding: var(--s2) var(--s5); border-radius: 99px; font-weight: 600; font-size: var(--fs-sm);
  background: var(--surface); box-shadow: inset 0 0 0 var(--border-w) var(--line);
  cursor: pointer; border: 0; text-decoration: none; color: var(--ink);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease);
}
.pill:hover { transform: translateY(-1px); box-shadow: inset 0 0 0 var(--border-w) var(--accent); }
.pill--primary { background: var(--btn-bg); color: var(--btn-ink); box-shadow: none; }
.pill--primary:hover { box-shadow: none; filter: brightness(1.08); }
/* awaiting an affiliate link, visibly not yet live, never a dead link pretending to work */
.pill--pending { opacity: .45; pointer-events: none; }
.pill--pending::after { content: " (link pending)"; font-weight: 400; font-size: .85em; }
.offer__perk { margin-top: var(--s3); font-size: var(--fs-sm); color: var(--ink-2); }
.offer__perk b { color: var(--ink); }
.offer__sub { margin-top: var(--s2); font-size: var(--fs-sm); color: var(--ink-2); }
.safetywing-price-widget { margin-top: var(--s4); }
/* .offer__disc removed, one affiliate disclosure now lives in the footer */
.foot__aff { margin-top: 0 !important; padding-bottom: var(--s2); }
/* Who operates this site. Quiet, but present on the page itself. */
.foot__entity { margin-top: var(--s2); opacity: .8; }

/* ---------- ⑤ how it works ---------------------------------------------- */
.how { padding-block: var(--s7); border-top: var(--hairline); margin-top: var(--s7); }
/* leading position: no rule above it, tighter to the hero it follows */
.how--lead { padding-block: 0 var(--s5); border-top: 0; margin-top: 0; }
.how--lead h2 { font-size: var(--fs-lead); color: var(--ink-2); font-weight: 600; letter-spacing: 0; }
.how--lead .how__steps { margin-top: var(--s3); }
.how__steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s5); margin-top: var(--s4); }
.step { display: flex; gap: var(--s3); align-items: flex-start; }
.step__n {
  flex: none; width: 30px; height: 30px; border-radius: 50%;
  display: grid; place-items: center; font-size: var(--fs-sm); font-weight: 700;
  background: var(--accent); color: var(--accent-ink);
}
.step p { font-size: var(--fs-sm); color: var(--ink-2); }
.step b { display: block; color: var(--ink); }

/* ---------- ⑥ trips ----------------------------------------------------- */
.trips { padding-block: var(--s6); }
.trips__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
.trips__title { min-width: 0; }
.trips__lede {
  margin-top: var(--s2); font-size: var(--fs-sm); color: var(--ink-2);
  max-width: 56ch; text-wrap: balance; line-height: 1.5;
}
.trips__lede b { color: var(--ink); font-weight: 600; }
/* Add-a-trip at the TOP as well as the bottom: with four trips on a phone the
   only add control sat below the fold, so the common action was the one you
   had to go looking for. */
.trips__acts { display: flex; align-items: center; gap: var(--s3); flex: none; }
.trips__add { flex: none; min-height: 44px; padding-inline: var(--s4); font-size: var(--fs-sm); }
/* Destructive, so it is outlined rather than filled and never sits where the
   eye lands first. It still asks before it does anything. */
.trips__clear {
  flex: none; min-height: 44px; padding-inline: var(--s4); font-size: var(--fs-sm);
  background: transparent; color: var(--bad);
  box-shadow: inset 0 0 0 var(--border-w) var(--line); font-weight: 600;
}
.trips__clear svg { width: 16px; height: 16px; flex: none; }
@media (hover: hover) and (pointer: fine) {
  .trips__clear:hover { background: var(--day-over-bg); box-shadow: inset 0 0 0 var(--border-w) var(--bad); filter: none; }
}
h2 { font-family: var(--font-display); font-size: var(--fs-h2); font-weight: var(--weight-display); letter-spacing: var(--track-h2); }

/* ---------- the 90/180 answer, above the list that produces it ----------
   Issue 1 of the trips plan, and the highest-value fix in it: this section
   collected trips and never once said what they added up to. */
.tsum {
  display: flex; align-items: center; gap: var(--s4) var(--s5); flex-wrap: wrap;
  margin-top: var(--s5); padding: var(--s4) var(--s5);
  background: var(--legend-bg); border: var(--hairline); border-radius: var(--radius);
}
.tsum__block { display: flex; flex-direction: column; gap: 2px; flex: none; }
.tsum__k { font-size: var(--fs-xs); font-weight: 650; color: var(--ink-2); }
.tsum__v { font-size: var(--fs-lead); color: var(--ink-2); font-variant-numeric: tabular-nums; line-height: 1.1; }
.tsum__v b {
  font-family: var(--font-numeral); font-size: 26px; font-weight: var(--weight-numeral);
  letter-spacing: var(--track-numeral); color: var(--ink);
}
.tsum__v--date { font-size: var(--fs-base); font-weight: 600; color: var(--ink); }
.tsum__bar { flex: 1 1 160px; min-width: 120px; height: 10px; border-radius: 99px; background: var(--bar-track); overflow: hidden; }
.tsum__bar i { display: block; height: 100%; border-radius: 99px; background: var(--ok); transition: width .4s var(--ease); }
.tsum__bar i[data-state="warn"] { background: var(--warn); }
.tsum__bar i[data-state="over"] { background: var(--bad); }
body.is-overstay .tsum__v b { color: var(--bad); }
/* the breach count, so an offending trip is visible without scrolling to it */
.tsum__flag {
  display: inline-flex; align-items: center; gap: .5em; flex: none;
  min-height: 38px; padding: 0 var(--s4); border-radius: 99px;
  background: var(--bad); color: #fff; text-decoration: none;
  font-size: var(--fs-sm); font-weight: 700;
}
.tsum__flag svg { width: 16px; height: 16px; flex: none; }

/* ---------- rows ------------------------------------------------------- */
.trips__cols { display: none; }
.trip {
  display: grid; gap: var(--s3) var(--s4);
  padding: var(--s4); border-radius: var(--radius-sm); background: var(--surface);
  border: var(--hairline); margin-top: var(--s3);
  grid-template-columns: 1fr 1fr;
  align-items: center;
}
/* ---------- empty state: one greyed card, action inside it -------------- */
.trip--empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: var(--s2); padding: var(--s7) var(--s5);
  background: var(--legend-bg);
  border: var(--border-w) dashed var(--line);
  color: var(--ink-2);
}
.trip__empty-icon {
  display: grid; place-items: center; width: 46px; height: 46px;
  border-radius: 50%; background: var(--surface); color: var(--ink-3);
  border: var(--hairline); margin-bottom: var(--s2);
}
.trip__empty-icon svg { width: 23px; height: 23px; }
.trip__empty-title { font-size: var(--fs-lead); font-weight: 700; color: var(--ink); }
.trip__empty-sub { font-size: var(--fs-sm); color: var(--ink-2); max-width: 46ch; }
.trip__empty-btn { margin-top: var(--s4); background: var(--surface); }
/* The dark numbered disc, the duplicate flag badge and the red "Delete" text
   link all lived here. The disc read as step 1 of a wizard, the flag was the
   second copy of information the destination selector already carried, and the
   delete was a text link stranded in the bottom-right corner of the card, as
   far from the trip's identity as it could get. All three are gone; see
   .trip__ord, .trip__state and .trip__del further down. */

/* ---------- ⑦ legend ---------------------------------------------------- */
.legend {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); align-items: center;
  padding: var(--s4); border-radius: var(--radius-sm); background: var(--legend-bg);
  font-size: var(--fs-sm); color: var(--ink-2); margin-top: var(--s5);
}
.legend i { display: inline-flex; align-items: center; gap: .5em; font-style: normal; }
.swatch { width: 18px; height: 18px; border-radius: 4px; border: var(--hairline); display: inline-block; }
.swatch--in { background: var(--day-in-bg); border-color: var(--day-in-border); }
.swatch--over { background: var(--day-over-bg); border-color: var(--bad); }
.swatch--num { display: grid; place-items: center; font-size: 10px; color: var(--ink-3); background: var(--surface); }
.swatch--badge {
  display: inline-grid; place-items: center;
  background: var(--day-in-bg); border-color: var(--day-in-border); font-size: 11px;
}

/* ---------- ⑧ calendar -------------------------------------------------- */
/* The `hidden` attribute is only `display:none` in the UA sheet, so ANY author
   `display` rule silently beats it. Every hidden element here, the trip editor,
   the offer cards, Clear trips, the note, carries a display rule, so all of them
   were rendering while "hidden". This must stay above the component rules. */
[hidden] { display: none !important; }

.cal { padding-block: var(--s6) var(--s8); }
/* Centred, unlike the other section heads, because this section is centred:
   the year control, the four-column grid and the footer control all sit on the
   same axis, and a left-aligned title on a 1440px measure would hang out well
   to the left of every other heading on the page. */
.cal__title { text-align: center; margin-bottom: var(--s4); }
.cal__sub { margin-top: var(--s2); font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.5; }
.cal__head { display: flex; align-items: center; justify-content: center; gap: var(--s4); margin-bottom: var(--s5); }
/* the fact the subtitle above does not carry, kept quieter than it: full-size
   body copy stranded between the year control and the grid read as a second
   heading for the calendar */
.cal__hint { margin: calc(var(--s5) * -0.5) 0 var(--s5); text-align: center; font-size: var(--fs-sm); color: var(--ink-2); }
.yearbtn {
  min-width: 44px; min-height: 44px; border-radius: var(--radius-sm); border: 0;
  background: var(--surface); box-shadow: inset 0 0 0 var(--border-w) var(--line); cursor: pointer; font-size: 18px;
}
.yearlabel { font-family: var(--font-numeral); font-size: var(--fs-h2); font-weight: var(--weight-numeral); font-variant-numeric: tabular-nums; min-width: 5ch; text-align: center; }

/* A calendar is a data object, not prose, it gets its own, wider measure.
   Twelve months at a 44px cell need 4 x 326px + gaps = 1352px to sit four across;
   forcing that into the 1140px reading measure is what pushed two columns off
   the right edge and made the whole document scroll sideways. */
.cal.wrap { max-width: min(1520px, 100%); }   /* 4 x (326px grid + 22px card) + gaps + 2 x 40px gutter */
.calendar { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--s5) var(--s4); }
/* No content-visibility here: 12 small grids don't need it, and `auto` breaks
   find-in-page, print, and full-page capture by skipping offscreen paint. */
/* Each month is a card. Twelve grids floating on the page ground shared one
   continuous background, so the only thing separating November from December
   was whitespace, and a run of trip days reading across the gap looked like one
   trip. A surface per month gives every grid an edge to end at — and it is what
   lets the day tints read as marks ON something rather than as patches of page. */
.month {
  break-inside: avoid;
  background: var(--surface);
  border: var(--hairline);
  border-radius: var(--radius);
  padding: var(--s4) var(--s3) var(--s3);
}
/* The month name is the only label in a field of numerals, so it earns weight
   and space rather than size, bigger type here would compete with the dates. */
.month__name {
  font-size: var(--fs-sm); font-weight: 700; text-align: center;
  letter-spacing: .02em; color: var(--ink);
  margin: 0 0 var(--s3); padding-bottom: var(--s2);
  border-bottom: var(--hairline);
}
.month__grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
/* Weekday initials are scaffolding: they must be readable and then disappear.
   Uppercase + tracking keeps single letters from reading as stray numerals. */
.wd {
  font-size: 10.5px; text-align: center; color: var(--ink-3);
  font-weight: 600; text-transform: uppercase; letter-spacing: .06em;
  padding-bottom: var(--s2);
}
.wd abbr { text-decoration: none; }

.day {
  aspect-ratio: 1 / 1; min-height: 44px; border: 0; background: transparent; cursor: pointer;
  border-radius: var(--radius-day); padding: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0;
  transition: background .12s var(--ease), color .12s var(--ease);
}
.day--pad { pointer-events: none; }
/* BUG (touch): a tapped day left a grey cell behind on an UNRELATED date.
   Touch browsers apply :hover to whatever was last touched and leave it there,
   and because adding a trip grows the list above the calendar, keepCalendarStill
   scrolls to compensate, so by the time the rebuilt grid settles the node under
   that screen point is a different day. The stuck hover then painted a date the
   visitor never touched, next to the one they did.
   Hover is a pointer affordance; gate it on having a pointer. */
@media (hover: hover) and (pointer: fine) {
  .day:hover:not(.day--pad) { background: var(--day-hover); }
}
.day__n { font-size: var(--fs-day); line-height: 1.1; font-variant-numeric: tabular-nums; }
.day__r { font-size: 11px; line-height: 1; color: var(--ink-3); font-variant-numeric: tabular-nums; min-height: 11px; }
/* The empty state shows 90 under every date on purpose: it is the honest
   answer before any trips exist, and it teaches what the number means before
   the visitor has to interpret a changing one. (An earlier version hid these
   as noise, that also hid the calendar's whole point on first load.) */
.day--in { background: var(--day-in-bg); color: var(--ink); }   /* black on the tint, green text on green reads as low-contrast decoration */
.day--in .day__n { font-weight: 700; }
.day--edge-s .day__n, .day--edge-e .day__n { font-weight: 800; }
.day--in .day__r { color: var(--ink-2); }
.day--preview { background: var(--day-preview); }
.day--over { background: var(--day-over-bg); color: var(--day-over-ink); }   /* red is the only colour a number takes */
.day--over .day__r { color: var(--day-over-ink); }
/* A day the visitor has PLANNED that is genuinely past 90, not merely a full
   day. It is the error the whole page is now built to catch, so it is the one
   cell that reverses out: solid red, white numerals. Non-colour signifier too,
   because "the red ones" is not a cue everybody gets. */
.day--breach { background: var(--bad); color: #fff; }
.day--breach .day__n { font-weight: 800; }
.day--breach .day__r { color: #fff; opacity: .9; }
.day--breach::after {
  content: ""; position: absolute; inset: 2px;
  border-radius: inherit; border: 1.5px dashed rgb(255 255 255 / .8);
  pointer-events: none;
}
.swatch--breach { background: var(--bad); border-color: var(--bad); }
.day--today { box-shadow: inset 0 0 0 2px var(--today-ring); font-weight: 700; }

/* ---------- ⑨ share ----------------------------------------------------- */
.share { padding-block: var(--s6); border-top: var(--hairline); display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; }
.share p { font-size: var(--fs-sm); color: var(--ink-2); margin-right: auto; }

/* the privacy claim, in the one place where a visitor is about to hand over
   their dates. Full-width so it never competes with the two buttons. */
.share__privacy {
  flex: 1 0 100%; margin-right: 0 !important;
  display: flex; gap: var(--s3); align-items: flex-start;
  margin-top: var(--s3); padding: var(--s4);
  background: var(--legend-bg); border-radius: var(--radius);
  font-size: var(--fs-xs) !important; line-height: 1.55;
}
.share__lock { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--ok); }
.share__privacy b { color: var(--ink); font-weight: 650; }

/* ---------- ⑩ SEO / FAQ ------------------------------------------------- */
.seo { padding-block: var(--s7); border-top: var(--hairline); }
.seo h2 { margin-bottom: var(--s4); }
.faq { border-bottom: var(--hairline); }
.faq summary { cursor: pointer; padding: var(--s4) 0; font-weight: 600; font-size: var(--fs-lead); list-style: none; display: flex; justify-content: space-between; gap: var(--s4); min-height: 44px; align-items: center; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; color: var(--ink-3); font-size: 22px; line-height: 1; }
.faq[open] summary::after { content: '−'; }
.faq p { padding-bottom: var(--s4); color: var(--ink-2); max-width: 68ch; }
/* An answer that ends in a link to the page with the long version. Bare
   anchors are unstyled in this stylesheet, so without this they are blue. */
.faq p a { color: var(--link); font-weight: 600; }
.faq code { background: var(--legend-bg); padding: .1em .4em; border-radius: 4px; font-size: .9em; }
.countries { display: flex; flex-wrap: wrap; gap: var(--s2); padding-bottom: var(--s4); }
.countries li {
  display: inline-flex; align-items: center; gap: .45em;
  font-size: var(--fs-sm); color: var(--ink-2);
  background: var(--legend-bg); padding: .25em .7em .25em .55em; border-radius: 99px;
}
/* the flag is the same emoji set the calendar and the trip rows already use, so
   a country reads the same way wherever it appears on the page. Decorative:
   the country name is right beside it, so the flag is aria-hidden in the markup. */
.countries .flag { font-size: 1.05em; line-height: 1; letter-spacing: 0; }

/* ---------- ⑪ footer ---------------------------------------------------- */
footer { padding-block: var(--s6) var(--s8); border-top: var(--hairline); font-size: var(--fs-sm); color: var(--ink-2); }
footer nav { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); }
footer a { color: var(--ink-2); min-height: 24px; display: inline-flex; align-items: center; }
footer p { margin-top: var(--s4); font-size: var(--fs-xs); color: var(--ink-3); max-width: 76ch; }

/* ---------- sticky answer bar ------------------------------------------ */
.sticky {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 20;
  background: var(--sticky-bg); color: var(--sticky-ink);
  border-top: var(--hairline);
  transform: translateY(101%); transition: transform .28s var(--ease);
  backdrop-filter: blur(10px);
}
.sticky.is-on { transform: none; }
/* One row, one height, whichever face is showing. The bar was wrapping to 191px
   because every child could wrap and three separate rules each claimed
   `margin-left:auto`; a fixed min-height and a single spacer keep it honest. */
.sticky .wrap {
  display: flex; align-items: center; gap: var(--s4);
  padding-block: var(--s3); flex-wrap: nowrap; min-height: 68px;
}
.sticky__stat { display: flex; align-items: baseline; gap: .4em; flex: none; }
.sticky b { font-family: var(--font-numeral); font-size: 24px; font-variant-numeric: tabular-nums; line-height: 1; }
.sticky__stat--date b { font-size: 19px; }
/* Labels were 11px uppercase at .08em tracking and .75 opacity, three legibility
   taxes stacked on the smallest text in the design, on the one element that is
   always on screen. Sentence case, full opacity, no tracking. */
.sticky span { font-size: var(--fs-sm); letter-spacing: 0; opacity: .8; }
/* the progress bar, mirrored from the answer panel so the state travels with you */
.sticky__bar {
  flex: 1 1 140px; min-width: 110px; height: 8px; border-radius: 99px;
  background: var(--bar-track); overflow: hidden;
}
.sticky__fill { height: 100%; border-radius: 99px; background: var(--ok); transition: width .45s var(--ease); }
.sticky__fill[data-state="warn"] { background: var(--warn); }
.sticky__fill[data-state="over"] { background: var(--bad); }
/* Both actions are the same height and vertically centred regardless of their
   contents, the share button loses its label under 640px and became a small
   square next to a full-width link. */
.sticky .btn {
  flex: none; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  /* An explicit height, not a minimum: the ghost variant carries a border and
     came out 2px taller than the solid one beside it. */
  height: 44px; padding-block: 0; padding-inline: var(--s4); white-space: nowrap;
}
/* Exactly one element opens the gap between content and actions. Never more
   than one. */
.sticky__actions { margin-left: auto; }
.sticky__actions { display: flex; align-items: center; gap: var(--s3); flex: none; }
/* two faces: a prompt until there is data, the numbers after */
.sticky__prompt { display: none; font-size: var(--fs-sm); margin: 0; }
.sticky__prompt b { font-family: inherit; font-size: inherit; font-weight: 700; }
body.is-empty .sticky__prompt { display: block; }
body.is-empty .sticky__stat,
body.is-empty .sticky__bar,
body.is-empty .sticky__actions { display: none; }
@media (max-width: 640px) {
  .sticky .wrap { gap: var(--s3) var(--s4); padding-block: var(--s2); }
  .sticky b { font-size: 20px; }
  .sticky__stat--date { display: none; }        /* the two counts matter most on a phone */
  .sticky__prompt { font-size: var(--fs-xs); }
  .sticky__bar { flex-basis: 100%; order: 9; }  /* full-width under the numbers */
}

/* ---------- responsive -------------------------------------------------- */
/* Breakpoints derived from the space a month actually needs (326px + gaps),
   not from round numbers, that mismatch is what clipped the last column.
   4-up needs 1432px of viewport, 3-up 1090px, 2-up 760px. */
@media (max-width: 1430px) { .calendar { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 1089px) { .calendar { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 900px)  { .how__steps { grid-template-columns: 1fr; gap: var(--s4); } }
@media (max-width: 759px) {
  /* one month, centred at its natural width, stretching it across the screen
     puts half a centimetre between adjacent days and destroys the week shape */
  .calendar { grid-template-columns: min(380px, 100%); justify-content: center; gap: var(--s6); }
}
@media (max-width: 560px) {
  .answer__cards { grid-template-columns: 1fr; }
  /* Value and label share a line; the offer link takes its own. Three items on
     one baseline squeezed "19 Aug" onto two lines inside its own card. */
  .stat { flex-direction: row; align-items: baseline; justify-content: space-between; flex-wrap: wrap; }
  .stat__v { white-space: nowrap; }
  .stat__link { flex: 1 0 100%; margin-top: var(--s2); align-self: flex-start; }
  .day { min-height: 46px; }
  .day__n { font-size: 17px; }
  .day__r { font-size: 12px; }
  /* Was 120px against a bar that measures ~132px with its action row and progress
     track. The last rows of December sat underneath it and could not be scrolled to. */
  body { padding-bottom: 152px; }
}

/* ---------- sticky bar on a phone --------------------------------------
   Rebuilt 2026-08-06 from two screenshots at 390px. The single nowrap row
   could not hold two stats, a progress track and two buttons: the affiliate
   CTA was clipped by the viewport edge, and `flex-basis:100%` on the progress
   track could never take effect inside a `flex-wrap:nowrap` parent, so the
   track was crushed to a sliver. Two explicit rows instead of one impossible
   one. */
@media (max-width: 640px) {
  .sticky .wrap {
    display: grid; flex-wrap: initial;
    grid-template-columns: auto auto 1fr;
    grid-template-areas: "left used actions" "bar bar bar";
    align-items: center; gap: var(--s2) var(--s4);
    padding-block: var(--s3); min-height: 0;
  }
  body.is-empty .sticky .wrap { grid-template-areas: "left left left" "bar bar bar"; }
  .sticky__stat:nth-of-type(1) { grid-area: left; }
  .sticky__stat:nth-of-type(2) { grid-area: used; }
  .sticky__prompt { grid-area: left; }
  .sticky__bar { grid-area: bar; flex-basis: auto; width: 100%; margin: 0; }
  /* One action group, right-aligned, sharing a single baseline, previously two
     equal-weight dark buttons sat side by side with no hierarchy between them,
     and the second one ran off the screen. */
  .sticky__share, .sticky > .wrap > .btn { grid-area: actions; }
  .sticky .wrap > .sticky__share ~ .btn { margin-left: 0; }
  .sticky__actions { grid-area: actions; display: flex; justify-content: flex-end; gap: var(--s2); min-width: 0; }
  .sticky__actions .btn { flex: none; min-height: 40px; padding-inline: var(--s4); }
  .sticky__actions .btn--ghost { color: var(--sticky-ink); box-shadow: inset 0 0 0 var(--border-w) currentColor; opacity: .85; }
  .sticky b { font-size: 22px; }
  .sticky__stat span { font-size: var(--fs-xs); opacity: .8; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

@media print {
  .sticky, .offer, .actions, .share { display: none; }
  .month { content-visibility: visible; }
}

/* ---------- trip rows --------------------------------------------------- */
.trip--open { border-color: var(--accent); border-style: dashed; }
.trip__hint { grid-column: 1 / -1; color: var(--accent); font-size: var(--fs-sm); font-weight: 600; }

/* identity: an ordinal you can read, not a dark numbered disc that looked like
   step 1 of a wizard, and the trip's state, past, ongoing or planned */
.trip__id { grid-column: 1 / -2; display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.trip__ord { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); white-space: nowrap; }
.trip__state {
  font-size: var(--fs-xs); font-weight: 650; line-height: 1;
  padding: 4px 9px; border-radius: 99px; white-space: nowrap;
  background: var(--legend-bg); color: var(--ink-2);
}
.trip__state--ongoing { background: var(--day-in-bg); color: var(--day-in-ink); }
.trip__state--planned { background: var(--stat-hero-bg); color: var(--ink-2); box-shadow: inset 0 0 0 1px var(--line); }
.trip__state--draft { background: var(--note-bg); color: var(--note-ink); }
/* Every trip card sits on the same white surface, whatever its state. Past
   trips were tinted with the page's warm paper and breach rows with a red wash,
   so a list of trips read as three different kinds of object. State is carried
   by the chip, the border and the copy instead, none of which need to change
   the paper the card is printed on. */
.trip--past { background: var(--surface); }
.trip--past .trip__ord { color: var(--ink-2); }
/* the row that broke the rule: red border, red numbers, red message, white card */
.trip--over { background: var(--surface); border-color: var(--bad); box-shadow: inset 3px 0 0 var(--bad); }
.trip--over .trip__ord { color: var(--bad); }

.trip__f { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.trip__lb { font-size: var(--fs-xs); font-weight: 650; color: var(--ink-2); }
.trip__sel {
  width: 100%; min-height: 44px; padding: 0 var(--s3); border-radius: var(--radius-sm);
  background: var(--field-bg); color: var(--ink);
  border: var(--border-w) solid var(--line); font: inherit; font-size: var(--fs-sm);
}
@media (hover: hover) and (pointer: fine) { .trip__sel:hover { border-color: var(--ink-3); } }

/* ---------- date field --------------------------------------------------
   A real <input type="date"> at zero opacity over the date written out in
   words. `type=date` renders in the BROWSER's locale, so 07/01/2026 read as
   1 July to an American and 7 January to everyone else, and this tool's whole
   audience is travelling to Europe, where a misparsed date becomes a wrong
   overstay answer. The input keeps the native picker, the keyboard, its
   accessible name and its min/max validation; only its rendering is replaced. */
.datefield { position: relative; display: block; }
.datefield__in {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer;
  font: inherit; color: transparent; background: transparent;
}
.datefield__t {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  min-height: 44px; padding: 0 var(--s3);
  border: var(--border-w) solid var(--line); border-radius: var(--radius-sm);
  background: var(--field-bg); font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.datefield__t b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.datefield__t svg { width: 17px; height: 17px; flex: none; color: var(--ink-3); }
/* The browser's own picker indicator, a ~17px target at the right edge, was the
   only part of a full-width field that opened the calendar, and it is invisible
   here because the input is transparent: people were aiming at our drawn icon
   and hitting it by luck of alignment. app.js opens the picker from a press
   anywhere in the field instead, so the indicator is removed rather than left
   as a second, differently-behaved hit area inside the first. Only where
   showPicker() exists to replace it, hence the class on <html>. */
.has-showpicker .datefield__in::-webkit-calendar-picker-indicator { display: none; }
.datefield--empty .datefield__t b { color: var(--ink-3); font-weight: 500; }
@media (hover: hover) and (pointer: fine) {
  .datefield__in:hover ~ .datefield__t { border-color: var(--ink-3); }
}
/* one focus treatment, shared by every control in a trip row and the editor,
   the country select used to carry a heavy green ring nothing else had */
.datefield__in:focus-visible ~ .datefield__t,
.trip__sel:focus-visible, .te__f .trip__sel:focus-visible {
  outline: 3px solid var(--focus); outline-offset: 2px; border-color: var(--focus);
}
.datefield__in:focus-visible { outline: none; }

.trip__len { font-size: var(--fs-sm); font-weight: 600; color: var(--ink); white-space: nowrap; }
/* what the trip COSTS, which "16 days" never said */
.trip__after { font-size: var(--fs-sm); font-weight: 700; color: var(--ok); font-variant-numeric: tabular-nums; white-space: nowrap; }
.trip__after span { font-weight: 500; color: var(--ink-2); }
.trip__after--over { color: var(--bad); }

.trip__del {
  grid-column: -2 / -1; justify-self: end; align-self: center;
  display: grid; place-items: center;
  width: 44px; height: 44px; padding: 0; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3); cursor: pointer;
}
.trip__del svg { width: 19px; height: 19px; }
@media (hover: hover) and (pointer: fine) {
  .trip__del:hover { background: var(--day-over-bg); color: var(--bad); }
}

/* Mobile card: two dates side by side, destination across, meta on one line.
   Was ~550px per trip for four fields with a dead zone above Delete. */
.trip__f--dur, .trip__f--after { flex-direction: row; align-items: baseline; gap: .5em; }
.trip__f--dur .trip__lb, .trip__f--after .trip__lb { font-weight: 600; }
/* explicit placement, never :nth-of-type, every child of a row is a <span>,
   so nth-of-type counted the identity block and the meta cells too and shifted
   every field one column to the right */
.trip__f--entry { grid-column: 1; }
.trip__f--exit  { grid-column: 2; }
.trip__f--dest  { grid-column: 1 / -1; }
.trip__f--dur   { grid-column: 1; }
.trip__f--after { grid-column: 2; }
.trip__over     { grid-column: 1 / -1; }

.day--anchor { box-shadow: inset 0 0 0 2px var(--accent); }

/* ---------- desktop: one row per trip -----------------------------------
   Four stacked fields per card meant two trips filled a 1080p viewport and the
   add control fell below the fold. Same template on the header and every row,
   so the columns line up and the per-field labels can go sr-only. */
@media (min-width: 760px) {
  .trips__cols, .trip {
    display: grid;
    grid-template-columns: minmax(96px, .85fr) minmax(132px, 1.05fr) minmax(132px, 1.05fr)
                           minmax(140px, 1.15fr) minmax(74px, .6fr) minmax(88px, .62fr) 44px;
    align-items: center; gap: var(--s3);
  }
  .trips__cols {
    margin-top: var(--s5); padding: 0 var(--s4) var(--s2);
    font-size: var(--fs-xs); font-weight: 650; color: var(--ink-2);
  }
  .trip { padding: var(--s2) var(--s4); margin-top: var(--s2); }
  .trip__lb { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .trip__id { grid-column: 1; grid-row: 1; flex-direction: column; align-items: flex-start; gap: 2px; }
  .trip__f { flex-direction: row; align-items: center; }
  .trip__f--entry { grid-column: 2; grid-row: 1; }
  .trip__f--exit  { grid-column: 3; grid-row: 1; }
  .trip__f--dest  { grid-column: 4; grid-row: 1; }
  .trip__f--dur   { grid-column: 5; grid-row: 1; }
  .trip__f--after { grid-column: 6; grid-row: 1; }
  .trip__del      { grid-column: 7; grid-row: 1; }
  .trip__hint { grid-column: 5 / 7; grid-row: 1; color: var(--accent); font-size: var(--fs-xs); }
  /* the breach message gets its own row under the fields it refers to */
  .trip__over { grid-column: 2 / 8; grid-row: 2; padding-bottom: var(--s2); }
  .datefield__t, .trip__sel { min-height: 38px; }
  .trip--add, .trip--empty { grid-template-columns: 1fr; }
}
/* the info button on the Duration column header */
.trips__cols-dur { display: inline-flex; align-items: center; gap: 5px; }
.info { position: relative; display: inline-flex; }
.info__b {
  width: 18px; height: 18px; border-radius: 50%; border: 0; padding: 0;
  display: grid; place-items: center; cursor: help;
  background: var(--line); color: var(--ink); font-size: 11px; font-weight: 700; font-style: italic;
}
.info__t {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  width: max-content; max-width: 260px; padding: var(--s3);
  background: var(--ink); color: var(--bg); border-radius: var(--radius-sm);
  font-size: var(--fs-xs); font-weight: 500; line-height: 1.45; letter-spacing: 0;
  opacity: 0; visibility: hidden; transition: opacity .14s var(--ease); z-index: 5;
}
/* keyboard-reachable, not hover-only */
.info__b:hover + .info__t, .info__b:focus-visible + .info__t { opacity: 1; visibility: visible; }

/* ---------- undo toast ---------------------------------------------------
   One line, always. It was a fixed-width box with an icon, a full sentence of
   dates and two buttons competing for a 390px screen, so the message collapsed
   into a five-line column beside the Undo button. A toast has one job and about
   eight seconds to do it: say what happened, offer the way back, get out. The
   dates are gone from the copy, the trip comes back with them attached, and
   the row above says them anyway. */
.toast {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(68px + var(--s4)); z-index: 40;
  display: flex; align-items: center; gap: var(--s2);
  width: max-content; max-width: calc(100% - 2 * var(--gutter));
  min-height: 50px; padding: 0 var(--s2) 0 var(--s5);
  background: var(--ink); color: var(--bg);
  border-radius: 99px; box-shadow: 0 12px 32px -10px rgb(14 20 30 / .5);
}
.toast__msg {
  min-width: 0; margin-right: var(--s3);
  font-size: var(--fs-sm); font-weight: 500; line-height: 1.2; letter-spacing: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.toast__undo {
  flex: none; border: 0; border-radius: 99px; cursor: pointer;
  height: 38px; padding: 0 var(--s5);
  background: var(--bg); color: var(--ink);
  font-family: inherit; font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0;
}
.toast__x {
  flex: none; border: 0; background: transparent; color: var(--bg);
  width: 38px; height: 38px; padding: 0; border-radius: 50%;
  font-size: 21px; line-height: 1; cursor: pointer; opacity: .6;
}
.toast__x:hover { opacity: 1; background: rgb(255 255 255 / .12); }

/* ---------- trip identifier on the calendar ---------------------------- */
/* Which block belongs to which trip is otherwise unreadable once there are
   more than two. Badge sits on the arrival and departure cells only. */
.day { position: relative; }
.day--badged { padding-top: 13px; }
.day__trip {
  position: absolute; top: 1px; left: 50%; transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 1px;
  line-height: 1; pointer-events: none; white-space: nowrap;
}
/* full colour on every day, a faded flag reads as "disabled", not "middle of a trip" */
.day__flag { font-size: 11px; }
.day--edge-s .day__flag, .day--edge-e .day__flag { font-size: 13px; }
/* square off the inner edge so a leg reads as one continuous block */
.day--in { border-radius: 0; }
.day--edge-s { border-top-left-radius: var(--radius-day); border-bottom-left-radius: var(--radius-day); }
.day--edge-e { border-top-right-radius: var(--radius-day); border-bottom-right-radius: var(--radius-day); }
@media (max-width: 560px) {
  .day__flag { font-size: 12px; }
  .day--edge-s .day__flag, .day--edge-e .day__flag { font-size: 15px; }
  .day--badged { padding-top: 15px; }
}
/* the date shrinks a touch on badged cells so the badge has room */

@media (max-width: 560px) {
  .trip__open { flex-wrap: wrap; }
  .trip__f { flex: 1 1 44%; }
  .trip__sel { max-width: none; width: 100%; }
}

/* ---------- share dialog ------------------------------------------------ */
.sd {
  border: 0; border-radius: var(--radius); padding: var(--s6);
  max-width: 560px; width: calc(100% - 2 * var(--gutter));
  background: var(--surface); color: var(--ink);
  box-shadow: 0 24px 60px -12px rgba(0,0,0,.35);
}
.sd::backdrop { background: rgba(10,14,20,.55); backdrop-filter: blur(3px); }
.sd h2 { font-family: var(--font-display); font-size: var(--fs-h2); margin-bottom: var(--s3); }
.sd__lede { font-size: var(--fs-sm); color: var(--ink-2); margin-bottom: var(--s5); }
.sd__x { position: absolute; top: var(--s3); right: var(--s3); }
.sd__x button {
  border: 0; background: transparent; font-size: 26px; line-height: 1;
  min-width: 44px; min-height: 44px; cursor: pointer; color: var(--ink-3);
}
.sd__x button:hover { color: var(--ink); }
.sd__label {
  display: block; font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; color: var(--ink-3); margin-bottom: var(--s2);
}
.sd__linkrow { display: flex; gap: var(--s2); }
.sd__url {
  flex: 1; min-width: 0; min-height: 44px; padding: 0 var(--s3);
  border: var(--border-w) solid var(--line); border-radius: var(--radius-sm);
  background: var(--legend-bg); color: var(--ink); font: inherit; font-size: var(--fs-sm);
  text-overflow: ellipsis;
}
.sd__url:focus { border-color: var(--accent); }
.sd__hint { font-size: var(--fs-xs); color: var(--ink-3); margin-top: var(--s2); }
.sd__preview { margin-top: var(--s5); }
.sd__preview img {
  width: 100%; border-radius: var(--radius-sm); border: var(--hairline); display: block; background: var(--field-bg);
}
.sd__label--send { margin-top: var(--s5); }
/* A grid, not a wrapping row: nine targets of different label lengths left a
   ragged last line with one lonely button on it. Equal cells, three up. */
.sd__actions {
  display: grid; gap: var(--s2); margin-top: var(--s3);
  grid-template-columns: repeat(3, 1fr);
}
.sd__actions .btn {
  min-height: 44px; padding-inline: var(--s3); font-size: var(--fs-sm);
  text-decoration: none; justify-content: flex-start; gap: .45em;
}
.sd__actions .btn > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the native sheet is the one that reaches Instagram and Messenger, so it
   leads and takes the full width */
.sd__native { grid-column: 1 / -1; justify-content: center !important; }
@media (max-width: 460px) { .sd__actions { grid-template-columns: repeat(2, 1fr); } }
.sd__empty {
  padding: var(--s5); border-radius: var(--radius-sm);
  background: var(--note-bg); border: 1px solid var(--note-border); color: var(--note-ink);
}
.sd__empty b { display: block; margin-bottom: var(--s2); }
.sd__empty p { font-size: var(--fs-sm); }
body.is-empty .sd__body { display: none; }
body:not(.is-empty) .sd__empty { display: none; }

/* ---------- icons ------------------------------------------------------- */
.ic { width: 17px; height: 17px; flex: none; }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: .5em; }
.sd__actions .btn .ic { width: 16px; height: 16px; }
/* On a phone every action loses its label and becomes the same 44px square.
   Three text buttons do not fit in 390px, and two of unequal width next to one
   icon chip read as a layout bug. The aria-label carries the name. */
@media (max-width: 640px) {
  .sticky__actions .btn > span { display: none; }
  .sticky__actions .btn {
    width: 44px; min-width: 44px; padding-inline: 0; gap: 0;
  }
  .sticky__actions .ic { width: 19px; height: 19px; }
}
.faq__src { font-size: var(--fs-xs); color: var(--ink-3); padding-bottom: var(--s4); }
.trips__add { min-height: 40px; padding-inline: var(--s4); font-size: var(--fs-sm); }
.trip--blank { border-style: dashed; border-color: var(--accent); }

/* solid share button, it is the primary action once a trip exists */
.btn--solid { background: var(--btn-bg); color: var(--btn-ink); box-shadow: none; }
.btn--solid:hover { filter: brightness(1.1); }
/* Was `margin-left: calc(var(--s2) - var(--s4))`, a NEGATIVE 10px, which pulled
   "Get covered" underneath the Share button on every desktop width. The gap on
   .sticky__actions owns the spacing; nothing needs to claw it back. */

/* ---------- add-a-trip row ----------------------------------------------
   A white card like the trip rows above it, because that is what it becomes
   when you press it. A dashed ghost read as disabled next to real rows. */
.trip--add { padding: 0; overflow: hidden; }
.trip__add {
  width: 100%; min-height: 56px; display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); cursor: pointer;
  background: transparent; color: var(--ink-2);
  border: 0; border-radius: var(--radius-sm);
  font-weight: 600; font-size: var(--fs-sm); font-family: inherit;
}
.trip__add:hover { color: var(--ink); background: var(--day-hover); }
.trip__add-plus {
  width: 26px; height: 26px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-size: 17px; font-weight: 700; line-height: 1;
  background: var(--legend-bg); color: var(--ink);
}
.trip__add:hover .trip__add-plus { background: var(--accent); color: var(--accent-ink); }
.offer__copy--dyn { margin-top: var(--s5); padding-top: var(--s5); border-top: var(--hairline); }
.offer--flights + .offer, .offer--flights { margin-top: 0; }
.offer--flights + .offer { margin-top: var(--s4); }
.offer__perk--todo { color: var(--note-ink); background: var(--note-bg);
  border: 1px solid var(--note-border); border-radius: var(--radius-sm);
  padding: var(--s2) var(--s3); font-size: var(--fs-xs); }
.offer__perk--todo code { background: var(--surface); padding: .1em .35em; border-radius: 3px; }
/* a plain text link, not a call-to-action button */
.stat__link {
  margin-top: var(--s2); font-size: var(--fs-xs); font-weight: 600;
  color: var(--link); text-decoration: underline; align-self: flex-start;
  min-height: 24px; display: inline-flex; align-items: center;
}
.stat__link:hover { text-decoration: underline; }
.faq__cta { padding-bottom: var(--s4); font-size: var(--fs-sm); }
.faq__cta a { color: var(--link); font-weight: 600; }
.faq__cta-note { color: var(--ink-3); }

/* ---------- clear-all confirmation --------------------------------------
   Deliberately the opposite weight to the trip editor: small, blunt, and it
   makes the safe choice the easy one. Cancel is focused on open, sits first,
   and stays the wider target; the destructive button is outlined rather than
   filled, so the eye does not land on it as "the button". */
.cd {
  border: 0; padding: var(--s6);
  width: min(440px, calc(100% - 2 * var(--gutter)));
  border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  box-shadow: 0 32px 70px -18px rgb(14 20 30 / .42);
  text-align: left;
}
.cd::backdrop { background: rgb(14 20 30 / .52); backdrop-filter: blur(3px); }
.cd[open] { animation: te-in .18s var(--ease); }
.cd__ic {
  display: grid; place-items: center; width: 42px; height: 42px;
  border-radius: 12px; background: var(--day-over-bg); color: var(--bad);
  margin-bottom: var(--s4);
}
.cd__ic svg { width: 22px; height: 22px; }
.cd h2 { font-family: var(--font-display); font-size: var(--fs-h2); letter-spacing: var(--track-h2); line-height: 1.2; }
.cd__body { margin-top: var(--s3); font-size: var(--fs-sm); color: var(--ink-2); line-height: 1.55; }
.cd__body b { color: var(--ink); font-weight: 650; }
.cd__body--warn {
  margin-top: var(--s4); padding: var(--s3) var(--s4);
  background: var(--day-over-bg); border-radius: var(--radius-sm);
  color: var(--day-over-ink);
}
.cd__body--warn b { color: var(--bad); }
.cd__foot { display: flex; gap: var(--s3); margin-top: var(--s5); }
.cd__keep { flex: 1 1 auto; justify-content: center; }
.cd__go {
  flex: none; background: transparent; color: var(--bad);
  box-shadow: inset 0 0 0 var(--border-w) var(--bad); font-weight: 650;
}
.cd__go:hover { background: var(--bad); color: #fff; filter: none; }
@media (max-width: 460px) {
  .cd { padding: var(--s5); }
  .cd__foot { flex-direction: column-reverse; }   /* safe choice nearest the thumb */
  .cd__go, .cd__keep { width: 100%; justify-content: center; }
}

/* ---------- ⑫ trip edit sheet -------------------------------------------
   Was a face of the sticky bar: a 68px strip holding two dates, a destination
   and a destructive action, with no scrim, no grabber and no exit that did not
   either commit or destroy. A sheet on a phone, a centred panel on a desktop,
   dismissible five ways, and it lifts off the page rather than growing out of
   the bottom edge of it. */
.te {
  border: 0; padding: 0;
  width: min(540px, calc(100% - 2 * var(--gutter)));
  max-height: calc(100dvh - 2 * var(--s5));
  border-radius: var(--radius);
  background: var(--surface); color: var(--ink);
  box-shadow: 0 32px 70px -18px rgb(14 20 30 / .42), 0 3px 10px rgb(14 20 30 / .10);
  overflow: auto; overscroll-behavior: contain;
}
/* the scrim: it says the calendar behind is not currently the thing you are
   using, which nothing said before */
.te::backdrop { background: rgb(14 20 30 / .50); backdrop-filter: blur(3px); }
.te[open] { animation: te-in .2s var(--ease); }
@keyframes te-in { from { opacity: 0; transform: translateY(10px) scale(.985); } }
.te--over { box-shadow: 0 32px 70px -18px rgb(14 20 30 / .42), 0 0 0 2px var(--bad); }

/* grabber, the affordance that says this can be dismissed. A real button, so
   it works for a pointer and a keyboard as well as a thumb. */
.te__grab {
  display: block; width: 100%; padding: 10px 0 4px;
  border: 0; background: transparent; cursor: grab;
}
.te__grab::before {
  content: ""; display: block; width: 36px; height: 5px; margin: 0 auto;
  border-radius: 99px; background: var(--line);
}
.te__grab:hover::before { background: var(--ink-3); }
/* inset, or a full-width button paints its ring across the whole top edge */
.te__grab:focus-visible { outline-offset: -5px; border-radius: var(--radius); }
.te:focus { outline: none; }
.te:focus-visible { outline: none; }

.te__head {
  display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s5) var(--s4);
}
.te__idw { margin-right: auto; min-width: 0; }
.te__head h2 { font-family: var(--font-display); font-size: var(--fs-h2); line-height: 1.15; letter-spacing: var(--track-h2); }
.te__sub { margin-top: 2px; font-size: var(--fs-sm); color: var(--ink-2); }
.te__state {
  flex: none; font-size: var(--fs-xs); font-weight: 650; line-height: 1;
  padding: 5px 10px; border-radius: 99px; white-space: nowrap;
  background: var(--legend-bg); color: var(--ink-2);
}
.te__state--ongoing { background: var(--day-in-bg); color: var(--day-in-ink); }
.te__state--planned { background: var(--field-bg); color: var(--ink-2); box-shadow: inset 0 0 0 1px var(--line); }
/* Delete: an icon in the header, as far from Done as the sheet allows. It used
   to sit ~150px from the primary confirm, told apart only by colour. */
.te__del {
  flex: none; display: grid; place-items: center;
  width: 44px; height: 44px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--ink-3); cursor: pointer;
}
.te__del svg { width: 20px; height: 20px; }
@media (hover: hover) and (pointer: fine) {
  .te__del:hover { background: var(--day-over-bg); color: var(--bad); }
}
.te__x { flex: none; }
.te__x button {
  border: 0; background: transparent; font-size: 26px; line-height: 1;
  width: 44px; height: 44px; cursor: pointer; color: var(--ink-3);
  border-radius: var(--radius-sm);
}
.te__x button:hover { color: var(--ink); background: var(--legend-bg); }

/* what the trip costs, the single highest-leverage line in the plan */
.te__impact {
  display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5);
  margin: 0 var(--s5); padding: var(--s4);
  background: var(--legend-bg); border-radius: var(--radius-sm);
  font-size: var(--fs-sm); color: var(--ink-2);
}
.te__impact b {
  font-family: var(--font-numeral); font-size: 21px; font-weight: var(--weight-numeral);
  letter-spacing: var(--track-numeral); color: var(--ink); font-variant-numeric: tabular-nums;
}
.te--over .te__impact b { color: var(--bad); }

.te__fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s3); padding: var(--s5); }
.te__f { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.te__f--wide { grid-column: 1 / -1; }
.te__lb { font-size: var(--fs-xs); font-weight: 650; color: var(--ink-2); }
.te__fields .datefield__t, .te__fields .trip__sel { min-height: 48px; font-size: var(--fs-base); }

.te__note {
  margin: calc(-1 * var(--s2)) var(--s5) 0; padding: var(--s3) var(--s4);
  border-radius: var(--radius-sm); background: var(--note-bg);
  border: 1px solid var(--note-border); color: var(--note-ink);
  font-size: var(--fs-sm); line-height: 1.5;
}
.te__note--over { background: var(--day-over-bg); border-color: var(--bad); color: var(--day-over-ink); font-weight: 600; }
.te__tip { margin: 0 var(--s5); font-size: var(--fs-xs); color: var(--ink-3); line-height: 1.5; }

.te__foot {
  display: flex; margin-top: var(--s5); padding: var(--s4) var(--s5);
  border-top: var(--hairline); background: var(--field-bg);
  position: sticky; bottom: 0;
}
.te__done { margin-left: auto; min-width: 132px; }

/* On a phone it is a sheet on the bottom edge, the thumb is already there. */
@media (max-width: 640px) {
  .te {
    width: 100%; max-width: none;
    position: fixed; inset: auto 0 0 0; margin: 0;
    max-height: 90dvh;
    border-radius: 16px 16px 0 0;
    transition: transform .18s var(--ease);
    /* the sheet's top edge cuts a calendar row mid-cell; fade the strip above
       it so the sliced row reads as "behind the sheet" and not as broken UI */
    box-shadow: 0 -26px 34px -12px rgb(14 20 30 / .30), 0 -1px 0 rgb(14 20 30 / .10);
  }
  .te[open] { animation: te-up .24s var(--ease); }
  @keyframes te-up { from { transform: translateY(100%); } }
  .te__head, .te__fields, .te__foot { padding-inline: var(--s4); }
  .te__impact, .te__note, .te__tip { margin-inline: var(--s4); }
  .te__done { width: 100%; min-height: 50px; }
  /* the sheet owns the bottom edge while it is open */
  body.is-editing .sticky.is-on { transform: translateY(101%); }
  body.is-editing .toast { bottom: auto; top: var(--s4); }
}

/* the trip being edited, marked on the calendar behind the scrim, the sheet
   is a preview of the edit, so the highlight has to be legible through it */
.day--sel { box-shadow: inset 0 0 0 2px var(--accent); }
.day--sel.day--edge-s, .day--sel.day--edge-e { box-shadow: inset 0 0 0 3px var(--accent); }

/* ---------- trip cards on a phone ---------------------------------------
   Target ~180px per card, down from ~550px: identity and delete share the
   header row, the two dates sit side by side, destination spans, and duration
   and window total share the last line. No dead space above the action. */
@media (max-width: 759px) {
  .trip { padding: var(--s3) var(--s4) var(--s4); gap: var(--s3) var(--s4); }
  .trip--empty, .trip--add { display: flex; }
  .trip__id { grid-column: 1 / -2; grid-row: 1; }
  .trip__del { grid-row: 1; }
  .tsum { gap: var(--s3) var(--s4); padding: var(--s4); }
  .tsum__bar { flex-basis: 100%; order: 9; }
  /* Both actions keep their words at every width. A bare trash can beside a
     labelled button is a guess, and this is the one control on the page that
     destroys everything. */
  .trips__acts { width: 100%; flex-wrap: wrap; }
  .trips__add, .trips__clear { flex: 1 1 150px; justify-content: center; padding-inline: var(--s3); }
}
@media (max-width: 380px) {
  .trip { grid-template-columns: 1fr; }
  .trip__f--entry, .trip__f--exit, .trip__f--dur, .trip__f--after { grid-column: 1; }
}

/* ---------- ⑫ article pages (/blog) --------------------------------------
   Blog posts are static files built by design/build.py, exactly like the
   calculator, and they load these same two stylesheets. So the masthead, the
   footer, the buttons and the type scale are the ones defined above; the only
   thing new here is a prose column.

   .post overrides the container rather than every child inside it: .wrap is
   1140px because the calendar needs that width, and a paragraph set that wide
   is unreadable. One measure for the article, set once. */
.post { max-width: 760px; padding-block: var(--s6) var(--s8); }

.post__crumb { font-size: var(--fs-sm); color: var(--ink-3); }
.post__crumb a { color: var(--ink-2); }

/* No font-size here on purpose: themes.css sets `h1 { font-size: calc(--fs-h1
   * .92) }` under the shipped theme, at a higher specificity than this rule,
   so anything set here would be a dead declaration. The article headline is
   the site's headline size, which is the right answer anyway. */
.post h1 {
  font-family: var(--font-display);
  line-height: 1.08;
  letter-spacing: var(--track-h1);
  font-weight: var(--weight-display);
  margin-top: var(--s4);
}
.post__lede { margin-top: var(--s4); font-size: var(--fs-lead); color: var(--ink); }
/* Date first, because on a page about rules that change, "when was this
   written" is the reader's first question about whether to trust it. */
.post__meta {
  margin-top: var(--s4); padding-bottom: var(--s5);
  border-bottom: var(--hairline);
  font-size: var(--fs-sm); color: var(--ink-3);
}

.post h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  letter-spacing: var(--track-h2);
  font-weight: var(--weight-display);
  margin-top: var(--s7); margin-bottom: var(--s3);
}
.post h3 {
  font-size: var(--fs-lead); font-weight: 650;
  margin-top: var(--s5); margin-bottom: var(--s2);
}
.post p { margin-top: var(--s4); color: var(--ink-2); }
.post p b, .post li b { color: var(--ink); font-weight: 600; }
.post a { color: var(--link); }

/* Two lists that carry the whole argument: who the rule catches and who it
   does not. Marked with a glyph rather than a bullet so the answer is legible
   from a scroll, and coloured from the same status tokens the calendar uses,
   so green and red mean here what they mean there. */
.post__list { display: grid; gap: var(--s4); margin-top: var(--s5); }
.post__list li { padding-left: var(--s6); position: relative; color: var(--ink-2); }
.post__list li::before {
  position: absolute; left: 0; top: 0;
  font-weight: 700; font-size: 1.05em;
}
.post__list--yes li::before { content: "\2713"; color: var(--ok); }
.post__list--no li::before { content: "\2715"; color: var(--bad); }

/* The regulation, quoted rather than paraphrased. The one place on the site
   where the source text appears verbatim, so it is set apart from our
   summary of it. */
.post__quote {
  margin-top: var(--s5); padding: var(--s4) var(--s5);
  border-left: 3px solid var(--accent);
  background: var(--legend-bg); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.post__quote p { margin-top: 0; color: var(--ink); }
.post__quote cite { display: block; margin-top: var(--s3); font-size: var(--fs-sm); font-style: normal; color: var(--ink-3); }

/* Limits and warnings, in the same amber the calculator uses for a caution
   note, so a reader who has used the tool already knows what the colour means. */
.post__note {
  margin-top: var(--s5); padding: var(--s4) var(--s5);
  background: var(--note-bg); border: 1px solid var(--note-border);
  border-radius: var(--radius); color: var(--note-ink);
}
.post__note p { color: inherit; }
.post__note p:first-child { margin-top: 0; }
.post__note a { color: inherit; text-decoration: underline; }
.post__note h2 { margin-top: 0; font-size: var(--fs-lead); }

/* The article exists to answer a question the calculator raises, so it ends by
   handing the reader back to the calculator. */
.post__cta {
  margin-top: var(--s7); padding: var(--s5);
  background: var(--surface); border: var(--border-w) solid var(--line);
  border-radius: var(--radius);
}
.post__cta h2 { margin-top: 0; }
.post__cta .btn { margin-top: var(--s4); }
/* A card already has an edge. A meta line inside one drew a second hairline
   just above it, which read as a broken divider on the guides index. */
.post__cta .post__meta { margin-top: var(--s3); padding-bottom: 0; border-bottom: 0; }

.post__src { margin-top: var(--s6); font-size: var(--fs-sm); color: var(--ink-3); }
.post__src h2 { font-size: var(--fs-lead); }
.post__src li { margin-top: var(--s3); }
.post__src a { color: var(--ink-2); }
