/* ============================================================================
   TRIPWAFFLE CUSTOM STYLES
   Combined universal + specific styles for performance
   ============================================================================ */

/* ============================================
   THEME SYSTEM - CSS CUSTOM PROPERTIES
   Dark mode (v3 - warm-neutral stone + refined complementary palette).
   Light mode is defined but dormant - shipped as a feature-flagged follow-up
   after native splash/status-bar config update. Toggling html.light activates.
   ============================================ */

/* <kbd> normalization. We use kbd in flight-hero (Term./Gate values) to
   carry semantic meaning, but the browser default font-family: monospace
   produces visible baseline drift next to the sans-serif "Term."/"Gate"
   labels and WebKit also adds a faint border outline on some versions.
   Force inheritance of the surrounding text so the value reads as part
   of the sentence rather than a chrome glyph. font-semibold via
   Tailwind handles weight; family/size/decoration come from the
   container. */
kbd {
	font: inherit;
	color: inherit;
	background: transparent;
	border: 0;
	box-shadow: none;
	padding: 0;
	margin: 0;
	text-decoration: inherit;
	text-transform: inherit;
	letter-spacing: inherit;
	line-height: inherit;
	white-space: inherit;
	display: inline;
	vertical-align: baseline;
	unicode-bidi: normal;
}


/* iOS native shell: bump root font-size so the WKWebView UI matches the
   visual weight of Android (where system font-scale + display density
   make 16px feel larger than on iOS at the same base). Set by the iOS
   shell's document-start bootstrap (class .tw-ios on <html>). Tailwind's
   rem-based utilities cascade off the root, so this scales the whole UI
   proportionally without touching individual rules. tw-larger-fonts wins
   if both are on - it's declared below so source order beats this. */
html.tw-ios {
	/* 19px root (118.75% of 16px). Higher than Apple HIG body (17pt) because
	   most of TripWaffle's chrome uses Tailwind text-sm (0.875rem) and
	   text-xs (0.75rem); bumping the root keeps those classes close to
	   Apple's body weight. text-sm at 19px root = ~16.6px, text-xs = ~14.2px.
	   Pushing the root higher (vs adjusting per-element) preserves the
	   intra-page typographic scale Tailwind establishes. */
	font-size: 118.75%;
}

/* Accessibility: larger fonts. Web/Android base is 16px -> 18px (+2). */
html.tw-larger-fonts {
	font-size: 112.5%; /* 18px base instead of 16px */
}

/* iOS base is already 19px (118.75%), so a flat 112.5% would SHRINK text
   for iOS users who enable larger fonts. Apply the same +2px bump on
   top of the iOS floor: 21px = 131.25%. Two-class selector wins on
   specificity over either single-class rule. */
html.tw-ios.tw-larger-fonts {
	font-size: 131.25%;
}

/* Header lettering logo - dark + light variants both inlined; toggle by class */
html:not(.light) .tw-logo-light { display: none; }
html.light .tw-logo-dark { display: none; }
html:not(.light) .tw-light-only { display: none; }
html.light .tw-dark-only { display: none; }

/* Dark theme (default) - warm-neutral stone surfaces, waffle-yellow accent */
:root {
	/* Tells the browser to render native form chrome (select text, date input
	   text, calendar popup, scrollbars) in the dark variant. Without this the
	   OS default applies and `<select>`/`<input type="date">` text falls back
	   to black-on-our-dark-bg — invisible on /admin pages and on the trip
	   create form. Light-mode override sits in the html.light block below. */
	color-scheme: dark;

	/* Border radius (matches rounded-lg for consistency) */
	--radius-base: 0.5rem;               /* 8px - used for cards and accordions */

	/* Component sizing (handoff doc) */
	--tw-radius-sm: 8px;
	--tw-radius-md: 12px;
	--tw-radius-lg: 18px;
	--tw-radius-xl: 24px;
	--tw-touch-target: 44px;
	/* Transition tokens. `fast`/`med` predate the rest; `snap`, `standard`,
	   `reveal`, `slow` were added 2026-05-05 to centralise the durations that
	   were previously hardcoded across nav, modal, and dropdown rules. */
	--tw-transition-snap: 60ms ease-out;       /* tap/press feedback */
	--tw-transition-fast: 120ms ease-out;      /* link hover, small state */
	--tw-transition-med: 180ms ease-out;       /* dropdown surfaces */
	--tw-transition-standard: 200ms ease-out;  /* nav reveal, dropdown */
	--tw-transition-reveal: 250ms ease-out;    /* content entry */
	--tw-transition-slow: 600ms ease;          /* header/nav scroll-hide */

	/* Surfaces - warm-neutral charcoal (R≥G≥B, mirrored from prior cool R≤G≤B) */
	--tw-bg-header: 22 20 18;            /* matches bg-elevated for chrome blend */
	--tw-bg-page: 17 15 14;              /* page background */
	--tw-bg-surface: 28 25 23;           /* cards, nav, components */
	--tw-bg-elevated: 37 34 30;          /* dropdowns, modals, popovers */
	--tw-bg-hover: 44 40 36;             /* hover states */
	--tw-bg-active: 59 54 49;            /* active/pressed states */

	/* Borders */
	--tw-border-default: 50 45 41;
	--tw-border-subtle: 42 38 34;
	--tw-border-accent: 226 137 30;      /* accent-500 */
	--tw-border-soft: rgba(255, 248, 240, 0.08);  /* slight warm white */

	/* Brand (warm-orange siblings) */
	--tw-brand: 232 120 18;
	--tw-brand-hover: 216 101 11;
	--tw-brand-soft: 244 164 60;
	--tw-brand-muted: 184 95 24;

	/* Event-type accents (separate from status) */
	--tw-flight-accent: 110 158 215;     /* steel blue - same hue family as the dark-mode pill fill (--tw-blue-500 = 38 80 126), but light enough to read as a text/glyph colour on dark backgrounds. Was indigo (92 124 250), which clashed with the navy pill on flight cards in dark mode. */
	--tw-transport-accent: 47 175 144;   /* muted teal, not neon green */
	--tw-hotel-accent: 192 108 132;      /* dusty rose, not error red */
	--tw-activity-accent: 230 162 60;    /* warm amber, brand-adjacent */
	--tw-food-accent: 217 138 74;        /* warm clay */
	--tw-note-accent: 154 127 209;       /* muted purple */

	/* Event-type backgrounds - soft tints, not full fills */
	--tw-flight-bg: rgba(92, 124, 250, 0.10);
	--tw-transport-bg: rgba(47, 175, 144, 0.10);
	--tw-hotel-bg: rgba(192, 108, 132, 0.10);
	--tw-activity-bg: rgba(230, 162, 60, 0.12);
	--tw-food-bg: rgba(217, 138, 74, 0.10);
	--tw-note-bg: rgba(154, 127, 209, 0.10);

	/* Status colors - reserved for pills/badges/alerts ONLY, never card bgs */
	--tw-status-confirmed: 63 191 127;
	--tw-status-pending: 217 164 65;
	--tw-status-warning: 224 122 63;
	--tw-status-error: 214 90 90;
	--tw-status-info: 92 124 250;

	/* Timeline + shadows */
	--tw-timeline-line: rgba(255, 255, 255, 0.12);
	--tw-shadow-card: 0 8px 24px rgba(0, 0, 0, 0.32);
	--tw-shadow-float: 0 16px 40px rgba(0, 0, 0, 0.45);

	/* Primary accent (waffle-yellow) - kept as brand */
	--tw-accent-950: 50 20 8;
	--tw-accent-900: 94 42 18;
	--tw-accent-800: 125 58 22;
	--tw-accent-700: 160 78 20;
	--tw-accent-600: 196 108 24;         /* canonical button bg */
	--tw-accent-500: 224 138 32;
	--tw-accent-400: 236 168 60;
	--tw-accent-300: 242 194 108;
	--tw-accent-200: 248 222 166;

	/* Text - warm-neutral white stack */
	--tw-text-primary: 244 241 236;
	--tw-text-secondary: 215 209 200;
	--tw-text-muted: 169 163 154;
	--tw-text-disabled: 124 118 111;

	/* Event-type color scales (drive bg-event-flight-*, bg-event-accommodation-*,
	   bg-event-transport-*, bg-event-generic-*). These used to double as status
	   colors; as of the Stage 6 decoupling they're event-only. */
	--tw-green-900: 14 44 26;            /* evergreen - transport bg */
	--tw-green-500: 22 78 50;             /* ~40% darker than the light-mode -500 (42 132 84). Green is the most luminous of the event-type hues so it pops more on dark pages than blue/pink/stone do; needs a heavier step. White text contrast ~9:1. */
	--tw-green-400: 134 216 160;
	--tw-green-200: 236 254 240;         /* bright cream-green - hero-text grade */
	--tw-orange-900: 100 40 26;
	--tw-orange-500: 226 102 60;
	--tw-orange-400: 232 126 90;
	--tw-orange-200: 250 214 196;
	--tw-red-900: 112 34 42;
	--tw-red-500: 220 82 86;
	--tw-red-400: 230 124 126;
	--tw-red-200: 250 212 212;
	--tw-yellow-900: 52 48 42;           /* warm stone (generic events) - NOT yellow */
	--tw-yellow-500: 92 86 78;            /* ~20% darker than light-mode -500 (118 110 100). Same dark-page-pop reasoning as the green token above. */
	--tw-yellow-400: 160 152 142;
	--tw-yellow-200: 240 232 216;

	/* Status color scales - dedicated (decoupled from event-type scales above).
	   Used by Tailwind status-* classes: status-info / success / warning /
	   caution / error / alert. Distinct hues so info ≠ flight-blue,
	   warning ≠ generic-stone, success ≠ transport-sage. */
	--tw-info-900: 18 58 66;             /* deep teal - info pill bg */
	--tw-info-500: 38 108 122;            /* ~20% darker so the En Route banner / airborne pill doesn't pop too loud on dark pages. White text contrast ~7:1. */
	--tw-info-400: 110 190 200;          /* text-on-dark */
	--tw-info-200: 210 232 236;          /* pale teal */
	--tw-success-900: 18 62 42;          /* deep emerald */
	--tw-success-500: 28 92 64;           /* ~40% darker (matches the heavier green step on --tw-green-500). Green is the most luminous of the status hues so it needs a deeper step than the others. */
	--tw-success-400: 104 198 150;
	--tw-success-200: 216 240 228;
	--tw-warning-900: 78 56 20;          /* deep mustard-amber */
	--tw-warning-500: 216 162 60;
	--tw-warning-400: 232 186 90;
	--tw-warning-200: 246 228 180;
	--tw-caution-900: 100 40 26;         /* coral-orange */
	--tw-caution-500: 226 102 60;
	--tw-caution-400: 232 126 90;
	--tw-caution-200: 250 214 196;
	--tw-error-900: 112 34 42;           /* crimson */
	--tw-error-500: 176 62 66;            /* ~20% darker so the Cancelled banner / pill reads as a serious crimson, not neon red. */
	--tw-error-400: 230 124 126;
	--tw-error-200: 250 212 212;
	--tw-alert-900: 76 40 104;           /* violet */
	--tw-alert-500: 134 70 168;           /* ~20% darker so the Diverted banner / pill doesn't glare against dark pages. */
	--tw-alert-400: 188 122 218;
	--tw-alert-200: 232 216 248;

	/* Event type card backgrounds - stone + subtle hue hint */
	--tw-card-flight: 30 36 44;          /* stone + steel-sky */
	--tw-card-accommodation: 42 32 33;   /* stone + dusty rose */
	--tw-card-transport: 32 40 36;       /* stone + sage */
	--tw-card-generic: 38 36 33;         /* stone, neutral */

	/* Event type colors (drive bg-event-* + text-event-* tokens) */
	--tw-blue-900: 28 50 80;             /* steel-sky bg (flight hero) */
	--tw-blue-500: 38 80 126;             /* ~20% darker than light-mode -500 (48 102 160). Same dark-page-pop reasoning as the green token above. */
	--tw-blue-200: 228 240 252;          /* bright cream - hero-text grade */
	--tw-pink-900: 92 40 48;             /* dusty rose bg (hotel hero) */
	--tw-pink-500: 144 76 82;             /* ~20% darker than light-mode -500 (182 96 104). Same dark-page-pop reasoning as the green token above. */
	--tw-pink-200: 252 228 226;
	--tw-purple-900: 76 40 104;          /* alert/diverted bg */
	--tw-purple-500: 168 92 206;
	--tw-purple-200: 232 216 248;

	/* Focus ring */
	--tw-ring-accent: 196 108 24;        /* accent-600 */

	/* Lifetime-stats country choropleth - cool blue scale so visited regions
	   don't compete with amber accents elsewhere on the page. Higher tier =
	   more visible on the theme's page bg (lighter on dark, darker on light). */
	--tw-map-unvisited: 46 43 41;        /* ~bg-elevated - subtle country shapes on dark */
	--tw-map-stroke: 22 21 20;           /* matches page bg for hairline separation */
	--tw-map-tier-1: 56 88 130;          /* 1 trip  - muted steel blue */
	--tw-map-tier-2: 76 122 176;         /* 2–3     - mid blue */
	--tw-map-tier-3: 112 162 214;        /* 4–6     - lighter */
	--tw-map-tier-4: 158 200 238;        /* 7+      - brightest, picks out home country */
}

/* Light theme (dormant - activated by html.light class once Stage 5b lands).
   Defined here so CSS-var scoping works without reshuffling the file.
   Accent-600 darkens in light mode for button white-text contrast;
   event-type -900/-200 values invert (bg becomes pale, text becomes dark). */
html.light {
	color-scheme: light;
	--tw-bg-header: 252 249 244;
	--tw-bg-page: 249 245 240;          /* warmed +2/-1/-4 from prior 247 246 244 */
	--tw-bg-surface: 255 253 249;
	--tw-bg-elevated: 243 236 226;
	--tw-bg-hover: 234 222 209;
	--tw-bg-active: 223 208 193;

	--tw-border-default: 229 219 207;
	--tw-border-subtle: 238 228 216;
	--tw-border-accent: 160 78 20;
	--tw-border-soft: rgba(41, 30, 20, 0.10);

	/* Brand inherits :root values; no light-mode override needed */

	/* Event-type accents - flight-accent stays on the original indigo in light
	   mode (user said light reads as perfect); only dark mode shifts to the
	   steel-blue family to match the navy pill. Other accents share the dark
	   value; backgrounds tinted lighter. */
	--tw-flight-accent: 92 124 250;
	--tw-flight-bg: rgba(92, 124, 250, 0.08);
	--tw-transport-bg: rgba(47, 175, 144, 0.08);
	--tw-hotel-bg: rgba(192, 108, 132, 0.08);
	--tw-activity-bg: rgba(230, 162, 60, 0.10);
	--tw-food-bg: rgba(217, 138, 74, 0.08);
	--tw-note-bg: rgba(154, 127, 209, 0.08);

	/* Timeline + shadows - warm tone over warm bg */
	--tw-timeline-line: rgba(35, 30, 24, 0.14);
	--tw-shadow-card: 0 8px 24px rgba(62, 47, 31, 0.08);
	--tw-shadow-float: 0 16px 40px rgba(62, 47, 31, 0.16);

	--tw-accent-950: 50 20 8;
	--tw-accent-900: 94 40 14;
	--tw-accent-800: 120 52 18;
	--tw-accent-700: 142 64 16;
	--tw-accent-600: 168 76 14;
	--tw-accent-500: 200 100 22;
	--tw-accent-400: 226 137 30;
	--tw-accent-300: 238 174 72;
	--tw-accent-200: 248 216 152;

	--tw-text-primary: 23 23 23;
	--tw-text-secondary: 52 48 43;
	--tw-text-muted: 111 105 98;
	--tw-text-disabled: 154 146 138;

	--tw-ring-accent: 168 76 14;

	--tw-card-flight: 238 244 250;
	--tw-card-accommodation: 250 238 238;
	--tw-card-transport: 238 248 240;
	--tw-card-generic: 244 240 232;

	--tw-blue-900: 188 216 238;
	--tw-blue-500: 48 102 160;
	--tw-blue-200: 20 46 82;
	--tw-pink-900: 238 208 206;
	--tw-pink-500: 182 96 104;
	--tw-pink-200: 88 32 38;
	--tw-green-900: 188 228 198;
	--tw-green-500: 42 132 84;
	--tw-green-400: 58 148 100;
	--tw-green-200: 8 48 24;
	--tw-yellow-900: 228 216 190;
	--tw-yellow-500: 118 110 100;
	--tw-yellow-400: 140 132 122;
	--tw-yellow-200: 48 44 40;
	--tw-orange-900: 252 224 210;
	--tw-orange-500: 208 84 44;
	--tw-orange-400: 188 72 34;
	--tw-orange-200: 96 32 22;
	--tw-red-900: 252 222 220;
	--tw-red-500: 200 68 68;
	--tw-red-400: 180 58 58;
	--tw-red-200: 110 28 38;
	--tw-purple-900: 244 226 250;
	--tw-purple-500: 150 68 178;
	--tw-purple-200: 70 30 86;

	/* Status - light mode (inverted: 900 is pale bg, 200 is dark text) */
	--tw-info-900: 216 238 240;
	--tw-info-500: 46 128 144;
	--tw-info-400: 60 144 160;
	--tw-info-200: 20 60 68;
	--tw-success-900: 220 240 228;
	--tw-success-500: 52 142 100;
	--tw-success-400: 68 158 116;
	--tw-success-200: 18 60 42;
	--tw-warning-900: 248 234 196;
	--tw-warning-500: 174 124 32;
	--tw-warning-400: 156 108 20;
	--tw-warning-200: 72 52 18;
	--tw-caution-900: 252 224 210;
	--tw-caution-500: 208 84 44;
	--tw-caution-400: 188 72 34;
	--tw-caution-200: 96 32 22;
	--tw-error-900: 252 222 220;
	--tw-error-500: 200 68 68;
	--tw-error-400: 180 58 58;
	--tw-error-200: 110 28 38;
	--tw-alert-900: 244 226 250;
	--tw-alert-500: 150 68 178;
	--tw-alert-400: 132 54 160;
	--tw-alert-200: 70 30 86;

	/* Country choropleth - light mode: deeper hues on light bg, inverted intensity */
	--tw-map-unvisited: 232 226 216;     /* stone-200 - subtle country shapes on light */
	--tw-map-stroke: 248 246 241;        /* matches page bg */
	--tw-map-tier-1: 180 204 228;        /* 1 trip  - pale blue */
	--tw-map-tier-2: 110 154 200;        /* 2–3 */
	--tw-map-tier-3: 58 110 170;         /* 4–6 */
	--tw-map-tier-4: 24 68 128;          /* 7+     - deepest */
}

/* ============================================
   DISPLAY FONT (Baloo 2)
   Applied globally to <h1> and non-micro <h2>. Use .font-display
   utility for non-heading elements that should render in the display
   face (e.g. Trip Map label, booking agent name).
   h2.uppercase is excluded so micro section-labels stay in body sans.
   ============================================ */
h1,
h2:not(.uppercase),
.font-display {
	font-family: "Baloo 2", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
	letter-spacing: -0.02em;
}

/* Input placeholder — muted version of the input text color (one tier
   lighter than primary). --tw-text-disabled was reading too pale in light
   mode and didn't read as "input hint" so much as "no content here";
   --tw-text-muted preserves the placeholder semantics while staying
   readable on bg-theme-hover / bg-theme-elevated input surfaces. */
input::placeholder,
textarea::placeholder,
select::placeholder {
	color: rgb(var(--tw-text-muted));
	opacity: 1;  /* Firefox defaults to 0.54; reset so token alpha is honored */
}

/*
* Theme-aware "soft glow" utilities.
* Replace hardcoded shadow-[0_0_Npx_rgba(0,0,0,opacity)] classes so the
* same element gets a dark halo in dark mode and a warm-stone halo in
* light mode. Keeps visual presence without the "harsh dark blob on
* cream" look in light mode.
*/
.tw-soft-glow { box-shadow: 0 0 12px rgba(0, 0, 0, 0.4); }
.tw-mini-glow { box-shadow: 0 0 6px rgba(0, 0, 0, 0.5); }
html.light .tw-soft-glow { box-shadow: 0 2px 10px rgba(100, 78, 55, 0.10), 0 0 1px rgba(100, 78, 55, 0.08); }
html.light .tw-mini-glow { box-shadow: 0 1px 4px rgba(100, 78, 55, 0.18); }

/* Header dropdowns (logo + user avatar): stronger lift via --tw-shadow-float
   token (theme-aware) plus a fade+scale entry animation.
   IMPORTANT: Flowbite/Popper sets `transform: translate(...)` inline on the
   dropdown to position it relative to the trigger. We must NOT animate the
   `transform` property (it would clobber Popper's positioning and the menu
   would briefly render at the page origin). Instead we animate the
   individual `scale` property, which composes with Popper's `transform`
   without overwriting it. */
.tw-header-dropdown {
	box-shadow: var(--tw-shadow-float);
	transform-origin: top center;
	animation: tw-header-dropdown-in 260ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* `divide-theme-border` resolves to the same RGB as `bg-theme-hover` in dark
   mode (both 77 64 54) so the dividers vanished against the new bg. Set them
   per theme: dark groove on warm-stone; light groove on cream. */
.tw-header-dropdown.divide-y > :not([hidden]) ~ :not([hidden]) { border-color: rgb(0 0 0 / 0.32); }
html.light .tw-header-dropdown.divide-y > :not([hidden]) ~ :not([hidden]) { border-color: rgb(62 47 31 / 0.10); }
/* Light mode lift: the panel was sharing `bg-theme-hover` (cream) with the
   trip-tile fallback bg AND the segmented-toggle's `bg-theme-elevated` pills,
   so everything dissolved into one cream wash. Bump the panel to the
   near-white surface token so the cream tiles + toggle pills inside register
   as elevated cards, the divider lines pick up contrast, and the existing
   warm shadow gets a clean canvas. Dark mode keeps its warm-stone bg. */
html.light .tw-header-dropdown { background-color: rgb(var(--tw-bg-surface)); }
/* "Create New Trip" CTA reads weakly in light mode: text-accent-300 over a
   12% accent wash on near-white sits well below WCAG AA. Bump to a deep
   accent-800 text + a slightly stronger wash so it actually feels like a
   primary call-to-action without going full-solid (still wants to be
   secondary to the trip tiles above). Dark mode keeps text-accent-300. */
html.light .tw-create-trip-cta { color: rgb(var(--tw-accent-800)); background-color: rgb(var(--tw-accent-600) / 0.14); }
html.light .tw-create-trip-cta:hover { color: rgb(var(--tw-accent-900)); background-color: rgb(var(--tw-accent-600) / 0.24); }
/* Appearance segmented toggle uses `border-black/25` for its outer ring +
   inter-pill dividers, which is a flat cool grey that reads harsh on the
   warm cream/white panel. Swap to a soft warm-brown rgba in light mode so
   the borders melt into the panel instead of carving it. Dark mode keeps
   the black/25 (works fine on the warm-stone bg there). */
html.light #user-menu-display-mode,
html.light #user-menu-display-mode > button { border-color: rgb(62 47 31 / 0.12); }
@keyframes tw-header-dropdown-in {
	from { opacity: 0; scale: 0.98; translate: 0 -6px; }
	to   { opacity: 1; scale: 1; translate: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
	.tw-header-dropdown { animation: none; }
}

/* Light mode: lift the top header above the warm-cream page bg.
   The default `bg-theme-header/50` resolves to ~252/249/244 at 50% opacity,
   which over the page (249/245/240) reads as visually identical. Pick a
   warm off-white roughly halfway between pure white and the page bg, at
   higher opacity so the lift reads through. backdrop-blur-sm keeps the
   frosted feel for content scrolling beneath. */
html.light #tw-header { background-color: rgb(252 250 248 / 0.85); }

/* Add-event dropdown chip hover halo. Per-type tinted ring on the round
   icon when its row is hovered. Two passes because the type tokens invert
   between modes — `-200` is the light tint in dark mode but flips to dark
   navy/maroon in light mode (where it'd read as "super dark"). Dark uses
   the light `-200` at moderate opacity; light uses the inverted `-900`
   (which is the pale tint in light mode) at higher opacity for visibility
   on the warm-cream dropdown bg. */
.tw-add-event-link:hover .tw-aec-flight        { box-shadow: 0 0 0 2px rgb(var(--tw-blue-200)   / 0.55); }
.tw-add-event-link:hover .tw-aec-accommodation { box-shadow: 0 0 0 2px rgb(var(--tw-pink-200)   / 0.55); }
.tw-add-event-link:hover .tw-aec-transport     { box-shadow: 0 0 0 2px rgb(var(--tw-green-200)  / 0.55); }
.tw-add-event-link:hover .tw-aec-generic       { box-shadow: 0 0 0 2px rgb(var(--tw-yellow-200) / 0.55); }
html.light .tw-add-event-link:hover .tw-aec-flight        { box-shadow: 0 0 0 2px rgb(var(--tw-blue-900)   / 0.85); }
html.light .tw-add-event-link:hover .tw-aec-accommodation { box-shadow: 0 0 0 2px rgb(var(--tw-pink-900)   / 0.85); }
html.light .tw-add-event-link:hover .tw-aec-transport     { box-shadow: 0 0 0 2px rgb(var(--tw-green-900)  / 0.85); }
html.light .tw-add-event-link:hover .tw-aec-generic       { box-shadow: 0 0 0 2px rgb(var(--tw-yellow-900) / 0.85); }

/* Day weather pill (tMin/tMax badge rendered next to each day's date).
   Knocked back in light mode so it doesn't compete with the day title. */
.day-weather-dry { background-color: rgba(41, 37, 36, 0.5); color: rgba(214, 211, 209, 0.85); }
.day-weather-rain { background-color: rgba(12, 74, 110, 0.25); color: rgba(125, 211, 252, 0.9); }
html.light .day-weather-dry { background-color: rgb(var(--tw-bg-hover)); color: rgb(var(--tw-text-muted)); }
html.light .day-weather-rain { background-color: rgb(var(--tw-info-900) / 0.5); color: rgb(var(--tw-info-200)); }

/* Calendar jump modal day cells in light mode: bg-theme-hover (the cell)
   sits on top of bg-theme-elevated (the modal panel) - the 9-14 RGB delta
   between the two cream tones makes empty in-trip days nearly invisible
   against the modal background. Two-tier lift so the grid is legible:
     - in-trip cells -> bg-theme-active   (the trip surface)
     - out-of-trip cells -> bg-theme-hover (faint outline, 30%-opacity
       disabled text alone is too washed-out on cream, unlike dark mode
       where the text contrasts the panel without a cell bg). */
html.light #calendar-jump-content [data-jump-date] {
	background-color: rgb(var(--tw-bg-active));
}

/* Flight-hero "LIVE UPDATES BEGIN ~48H BEFORE FLIGHT" hint and the
   duration/distance line that takes its place when live data is available:
   small mono eyebrow styling (uppercase via CSS so source text stays editable). */
[data-flight-tracking-hint],
[data-flight-hero-meta] {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.625rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
}
/* Flight-hero origin/destination city labels styled to match the mono
   eyebrow language used by the tracking hint. Lower letter-spacing so
   real-word names don't run wide and break the truncate. */
[data-flight-hero-city] {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.75rem;
	letter-spacing: 0.1em;
	text-transform: uppercase;
}
/* Disabled-tier reads too pale on bg-theme-hover in light mode; bump
   contrast a notch (closer to muted). Dark mode unchanged. */
html.light [data-flight-tracking-hint],
html.light [data-flight-hero-meta] {
	color: rgb(130 122 114);
}
html.light #calendar-jump-content .aspect-square:not([data-jump-date]) {
	background-color: rgb(var(--tw-bg-hover));
	border-radius: 0.375rem;
	opacity: 1;
	color: rgb(var(--tw-text-muted));
}

/* Trip-day info pills (flight duration, layover, transport duration, drive
   time, "Nearby", distance). Default-tier pills use bg-theme-elevated/50 +
   tracking-wide; in light mode the dark-mode contrast translates to nearly
   black text on a cream chip, which reads heavier than these calm-info pills
   should. Knock the text back to muted (label) / secondary (value) only on
   the default-tier chips - status-warning / status-caution layover variants
   keep their punchy alert contrast because they have a different bg class. */
html.light [id^="collapse-day-"] li > .inline-flex.tracking-wide.bg-theme-elevated\/50,
html.light [id^="collapse-day-"] li > a.inline-flex.tracking-wide.bg-theme-elevated\/50,
html.light [id^="collapse-day-"] li > .inline-flex.tracking-wide.bg-theme-elevated\/50 .text-theme-text-secondary,
html.light [id^="collapse-day-"] li > a.inline-flex.tracking-wide.bg-theme-elevated\/50 .text-theme-text-secondary {
	color: rgb(var(--tw-text-muted));
}
html.light [id^="collapse-day-"] li > .inline-flex.tracking-wide.bg-theme-elevated\/50 .text-theme-text,
html.light [id^="collapse-day-"] li > a.inline-flex.tracking-wide.bg-theme-elevated\/50 .text-theme-text {
	color: rgb(var(--tw-text-secondary));
}

/*
* Trip destination widgets (UK/Montenegro/etc cards in the horizontal carousel).
* Dark mode uses bg-accent-950/30 which reads as a warm dirty-tan in light mode -
* replace with a clean elevated surface + subtle accent wash so the colored
* status dots read cleanly.
*/
html.light .widget-fade-in {
	background-color: rgb(var(--tw-bg-surface));
	border-color: rgb(var(--tw-border-default));
}

/* Vertical timeline event icons (left rail). Cut-out "ring" around each
   colored circle - color must match the day-card body surface so the ring
   reads as a clean cut-out (not a tinted halo). Both modes follow
   bg-theme-surface; the `var(--tw-day-bg)` cascade at line 3754 also
   uses this fallback when --tw-day-bg is undefined. */
.tw-tl-ring { --tw-ring-color: rgb(var(--tw-bg-surface)); }
html.light .tw-tl-ring { --tw-ring-color: rgb(var(--tw-bg-surface)); }

/* Event comment ("user note" speech-bubble pill inside a trip-timeline
   event card). `text-accent-300/80` reads too light on the pale accent
   bg in light mode - bump to accent-700 for readable contrast. Dark mode
   unchanged. */
html.light .tw-event-note p {
	color: rgb(var(--tw-accent-700));
}

/* Trip-timeline event-card pills (IATA, confirmation, seat). Uses a
   neutral "frost" tone that lifts off any card-type bg without claiming
   the card's own color - a same-family tint (tried earlier) reads too
   flat on top of the tinted card. Scoped to all bg-card-* so flight /
   accommodation / transport / generic all get consistent pills.

   Hover is intentionally narrow: only elements with `hover:bg-theme-active`
   (i.e. the clickable confirmation-code kbd) get a hover state. Read-only
   pills (seat number, IATA code) are `bg-theme-hover` without the hover
   class, so they stay static - no misleading interactive affordance. */
[class*="bg-card-"] .bg-theme-hover {
	background-color: rgba(255, 255, 255, 0.08);
	border-color: rgba(255, 255, 255, 0.14);
}
[class*="bg-card-"] .hover\:bg-theme-active:hover {
	background-color: rgba(255, 255, 255, 0.14);
}
html.light [class*="bg-card-"] .bg-theme-hover {
	background-color: rgba(30, 20, 15, 0.06);
	border-color: rgba(30, 20, 15, 0.15);
}
html.light [class*="bg-card-"] .hover\:bg-theme-active:hover {
	background-color: rgba(30, 20, 15, 0.1);
}

/* Event-thumb backdrop. Airline / ride-share logos are designed for
   dark surfaces, but stark black against a dark-event-card reads as a
   harsh rectangle. Swap to a mid tone in both modes so the thumb blends
   with its surface without muting the logo itself.
.tw-logo-thumb scopes the contained-logo case (object-contain + p-1.5);
.tw-thumb-wrap matches photo-thumb wrappers (object-cover) so the
   loading/LQIP-fade state and any letterboxing match the logo backdrop.
   Dark:  bg-theme-surface (stone-900) - matches trip-card surface, sits
          naturally against bg-card-{type}/90 event rows.
   Light: warm mid-stone - matches flight-event-hero airline-logo treatment
          (bg-black/30 on a light card reads as ~mid-gray). */
.tw-logo-thumb,
.tw-thumb-wrap { background-color: rgb(var(--tw-bg-surface)); }
html.light .tw-logo-thumb,
html.light .tw-thumb-wrap { background-color: rgb(var(--tw-bg-active)); }

/* Stats-page airline logo thumbs. Dark mode keeps bg-white so the
   white-on-colored brand logos read cleanly against the dark surface;
   in light mode a white box disappears into the near-white page, so
   swap to a mid-stone wash (same tone as tw-logo-thumb). */
html.light .tw-airline-thumb { background-color: rgb(var(--tw-bg-active)); }

/* Stats-page dismissable tip chip. The "Find out how" pill uses
   bg-accent-700/30 text-accent-400 - reads correctly as bright orange on
   dark orange in dark mode, but in light mode accent-400 (bright orange)
   on an accent-700 wash is low-contrast. Swap to accent-700 text for
   readable dark-on-pale. Also fixes the leading icon's contrast. */
html.light [data-tip] .text-accent-400 { color: rgb(var(--tw-accent-700)); }
html.light .tw-new-user-email { color: rgb(var(--tw-accent-800)); }
html.light .tw-new-user-email:hover { color: rgb(var(--tw-accent-900)); }

/* Codeshare Flight notice on /event/{id}. The card uses a bg-accent-900/20
   wash that lands as a pale peach in light mode; accent-300 heading +
   accent-400 icon both read as washed-out gold against it. Bump both to
   accent-700 for readable dark-amber on cream. Dark mode unchanged. */
html.light .tw-codeshare-notice .text-accent-300,
html.light .tw-codeshare-notice .text-accent-400 {
	color: rgb(var(--tw-accent-700));
}

/* Class-wide fix for `text-stone-900 bg-accent-{600,700}` button pattern.
   In dark mode accent-600 (#C46C18) reads as a bright-orange CTA and stone-900
   provides ~4.3:1 contrast — passes WCAG AA. In light mode accent-600 (#A84C0E)
   and accent-700 (#8E4010) are dark amber/brown and stone-900 (#1C1917) is
   near-black, giving 2.4:1 — fails AA hard. Sweep all such buttons to white
   text in light mode. Affects: stats year-filter (.tw-year-btn), dashboard
   sample-trip CTA, push/enable buttons, /settings/{delete-account,subscriptions,
   diagnostics} CTAs, header avatar fallback, tips author avatars. */
html.light .bg-accent-600.text-stone-900,
html.light .bg-accent-700.text-stone-900 {
	color: #fff !important;
}

/* ----------------------------------------------------------------------------
   Raw-red → status-error sweep
   The codebase has ~15 destructive buttons + banners using raw Tailwind red-*
   classes (red-700, red-900/30, red-400 etc.). Raw red doesn't theme — light
   mode renders them as muddy maroon against the warm-cream page bg. Map each
   recurring pattern to status-error tokens so they stay theme-aware. Targeted
   class-combo selectors so ad-hoc red usages elsewhere are untouched.
   ---------------------------------------------------------------------------- */

/* Solid destructive button — `bg-red-700 hover:bg-red-600 text-white`
   (settings/account delete CTA, settings/delete-account confirm) */
.bg-red-700 {
	background-color: rgb(var(--tw-error-500)) !important;
}
.bg-red-700.hover\:bg-red-600:hover {
	background-color: rgb(var(--tw-error-500)) !important;
	filter: brightness(0.9);
}

/* Quiet destructive secondary — `text-red-400 bg-red-900/20 border-red-800/40
   hover:bg-red-900/40` (calendar revoke, trip share revoke) */
.text-red-400.bg-red-900\/20 {
	color: rgb(var(--tw-error-500)) !important;
	background-color: rgb(var(--tw-error-500) / 0.10) !important;
}
.bg-red-900\/20.border-red-800\/40 {
	border-color: rgb(var(--tw-error-500) / 0.30) !important;
}
.text-red-400.bg-red-900\/20.hover\:bg-red-900\/40:hover {
	background-color: rgb(var(--tw-error-500) / 0.18) !important;
}

/* Ghost destructive icon button — `text-red-400 hover:text-red-300` paired
   with optional `hover:bg-red-900/20` (settings/sharing remove, trip share
   leave/cancel/remove, email-aliases remove) */
.text-red-400.hover\:text-red-300 {
	color: rgb(var(--tw-error-500)) !important;
}
.text-red-400.hover\:text-red-300:hover {
	color: rgb(var(--tw-error-400)) !important;
}
.hover\:bg-red-900\/20:hover {
	background-color: rgb(var(--tw-error-500) / 0.12) !important;
}

/* Toast banner — `bg-red-900/30 border-red-700 text-red-300` (calendar,
   sharing, trip-share, delete-account confirmation banners) plus the
   green-success sibling */
.bg-red-900\/30.border-red-700,
.bg-red-900\/30.text-red-300 {
	background-color: rgb(var(--tw-error-500) / 0.12) !important;
	border-color: rgb(var(--tw-error-500) / 0.40) !important;
	color: rgb(var(--tw-error-500)) !important;
}
.bg-red-900\/40.border-red-800 {
	background-color: rgb(var(--tw-error-500) / 0.14) !important;
	border-color: rgb(var(--tw-error-500) / 0.45) !important;
}
.bg-red-900\/40.text-red-300 {
	color: rgb(var(--tw-error-500)) !important;
}
.bg-green-900\/30.border-green-700,
.bg-green-900\/30.text-green-300 {
	background-color: rgb(var(--tw-success-500) / 0.12) !important;
	border-color: rgb(var(--tw-success-500) / 0.40) !important;
	color: rgb(var(--tw-success-500)) !important;
}

/* Status-tone pills in settings + admin: raw `bg-{green,sky}-900[/40]` +
   light pastel text reads as muddy / no-contrast on cream. Scope these to
   `html.light` so dark mode keeps the original pill, light mode gets a
   readable success/info skin via themed CSS vars.
   Sites: settings/account (Active sub), settings/diagnostics + subscriptions
   (Android/iOS platform pills), settings/email-aliases, admin/cloudmailin-logs,
   admin/email-queue, admin/emails (success badges). */
html.light .bg-green-900\/40.text-green-300,
html.light .bg-green-900.text-green-200,
html.light .bg-green-900.text-green-200.border-green-700,
html.light .border-green-700.text-green-200 {
	background-color: rgb(var(--tw-success-500) / 0.14) !important;
	border-color: rgb(var(--tw-success-500) / 0.40) !important;
	color: rgb(var(--tw-success-500)) !important;
}
html.light .bg-sky-900.text-sky-200,
html.light .bg-sky-900.text-sky-200.border-sky-700 {
	background-color: rgb(var(--tw-blue-500) / 0.14) !important;
	border-color: rgb(var(--tw-blue-500) / 0.40) !important;
	color: rgb(var(--tw-blue-500)) !important;
}

/* Admin status badges using raw 100/800 light-mode shades from Tailwind
   (`bg-green-100 text-green-800` / `bg-red-100 text-red-800`). Dark mode keeps
   them as-is (pastel-on-dark is readable). Light mode rewrites to themed
   success / error tokens so the pill matches the rest of the cream UI.
   Sites: admin/cloudmailin-outbound-logs (Sent / Failed badges). */
html.light .bg-green-100.text-green-800 {
	background-color: rgb(var(--tw-success-500) / 0.14) !important;
	color: rgb(var(--tw-success-500)) !important;
}
html.light .bg-red-100.text-red-800 {
	background-color: rgb(var(--tw-error-500) / 0.14) !important;
	color: rgb(var(--tw-error-500)) !important;
}

/* Danger-zone container — `bg-red-950/30 border-red-900/50` (settings/account
   delete-account panel) */
.bg-red-950\/30 {
	background-color: rgb(var(--tw-error-500) / 0.08) !important;
}
.border-red-900\/50,
.bg-red-950\/30.border-red-900\/50 {
	border-color: rgb(var(--tw-error-500) / 0.30) !important;
}

/* ----------------------------------------------------------------------------
   Tip / info-pill skin
   `inline-flex ... bg-accent-900/15 border-accent-700/20` with a
   `bg-accent-700/25` icon backplate — used for onboarding tips on dashboard,
   stats imports nudge, email-aliases footer, trip-page push tip, and trip
   empty-state. In light mode this composites as muddy mid-beige and misuses
   brand-orange for an informational surface. Re-skin to a clean neutral pill
   with a soft brand-tinted icon backplate.
   ---------------------------------------------------------------------------- */
.bg-accent-900\/15.border-accent-700\/20,
.bg-accent-900\/20.border-accent-700\/30 {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border-color: var(--tw-border-soft) !important;
	box-shadow: var(--tw-shadow-card);
}
.bg-accent-900\/15.border-accent-700\/20 .bg-accent-700\/25,
.bg-accent-900\/20.border-accent-700\/30 .bg-accent-700\/25 {
	background-color: rgb(var(--tw-brand) / 0.14) !important;
}

/* Busyness-widget progress-bar track. Sits inside a colored container
   (bg-status-{success|warning|error}-900/30) so needs to read on both
   pale-green/amber/red (light) and deeper-tinted (dark) backdrops.
   bg-theme-hover was too pale in light mode - swap to a subtle stone
   wash that darkens the track enough to visibly recess under the fill. */
.tw-busyness-track { background-color: rgba(0, 0, 0, 0.18); }
html.light .tw-busyness-track { background-color: rgba(60, 40, 30, 0.15); }

/* Day-accordion content panel, photo-backed variant. Uses
   `bg-theme-elevated/50 backdrop-blur-[2px] backdrop-brightness-50` from
   trip/_day_accordion.php - the half-brightness wash darkens the photo
   behind timeline content to keep event cards readable in dark mode.
   In light mode that reads as a heavy grey slab against cream, so we
   invert: warm-cream wash instead of darken, with the blur retained.
   Narrowed to `.rounded-b-xl` so only the content container matches
   (empty-day h2 uses rounded-base + gets its own chrome below). */
html.light .bg-theme-elevated\/50.backdrop-brightness-50.rounded-b-xl {
	background-color: rgb(var(--tw-bg-page) / 0.67);
	--tw-backdrop-brightness: brightness(0.99);
}

/* ============================================
   UNIFIED MODAL SHELL
   Consistent backdrop, centering, z-index, and surface treatment across
   every modal in the app. Drop-in classes:
     <div class="tw-modal">              - outer: full-viewport, centered
       <div class="tw-modal-surface">    - inner: panel shell
         ...
       </div>
     </div>
   Rationale: previously z-indexes ranged 50/55/70/100, backdrop opacity
   50/60/70, and some modals used items-start pt-16 vs items-center. That
   caused jumpy-feeling modals and inconsistent positioning.
   ============================================ */
.tw-modal {
	position: fixed;
	inset: 0;
	z-index: 1050;                    /* above bottom-nav z-[1001] */
	display: none;
	/* `safe center` falls back to flex-start when the modal surface is taller
	   than the available flex space - which happens on mobile when the keyboard
	   opens and the visual viewport shrinks. Plain `center` clips the top of
	   overflowing flex children in a way that can't be scrolled into view, so
	   the modal's header slides under the fixed #tw-header.
	   Plain `center` listed first as a fallback: engines that don't recognise
	   the `safe` keyword drop the entire `align-items: safe center` declaration,
	   defaulting align-items to `stretch` which deforms the surface. */
	align-items: center;
	align-items: safe center;
	justify-content: center;
	/* Reserve space for the fixed #tw-header (~48px) and #tw-bottom-nav (~72px)
	   so tall modals don't extend behind them. Both bars are translucent w/
	   backdrop-blur, which otherwise makes modal top/bottom appear obscured. */
	padding: calc(var(--tw-safe-top, 0px) + 4rem) 1rem calc(var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px)) + 5.5rem);
	background: rgba(0, 0, 0, 0.6);
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
	overflow-y: auto;
	/* When a modal input is focused on mobile, iOS auto-scrolls this container
	   to bring the input into view - without scroll-padding the modal top slides
	   under the fixed #tw-header and #tw-bottom-nav. Mirror the flex padding so
	   auto-scroll stops short of the chrome. */
	scroll-padding-top: calc(var(--tw-safe-top, 0px) + 4rem);
	scroll-padding-bottom: calc(var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px)) + 5.5rem);
}
/* When the soft keyboard is up, twInitializeKeyboardNavHide adds
   .tw-keyboard-open on <html> and sets --tw-keyboard-h to the keyboard's
   height in CSS px (innerHeight - visualViewport.height). We bolt that
   onto the modal's bottom padding so `align-items: safe center` ends up
   centering the surface in the visible area instead of the full layout
   viewport. No native code involvement - works on any platform whose
   visualViewport reflects the keyboard, including iOS Safari. */
html.tw-keyboard-open .tw-modal {
	/* Drop the 5.5rem #tw-bottom-nav reservation: twInitializeKeyboardNavHide
	   sets display:none on the bottom-nav whenever the keyboard is up, so
	   the space is unused chrome that was squishing the surface (body has
	   flex:1 1 auto + overflow-y:auto, so it shrinks textarea/radios into
	   a scroll well when the surface is capped). 1rem is enough breathing
	   room above the keyboard keys; safe-bottom usually reads 0 with the
	   keyboard up but harmless when not. */
	padding-bottom: calc(var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px)) + var(--tw-keyboard-h, 0px) + 1rem);
	scroll-padding-bottom: calc(var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px)) + var(--tw-keyboard-h, 0px) + 1rem);
}
/* Show when .hidden class is removed (works with both Flowbite's data-modal-*
   flow - which toggles .hidden and adds .flex - and the app's custom modal
   JS that just toggles .hidden). Animation runs once each time the
   .hidden class is removed (no JS hook needed). Closing is instant because
   display:none nukes any out-animation; that's an acceptable asymmetry -
   the user perceives the open transition far more than the close. */
.tw-modal:not(.hidden) {
	display: flex;
	animation: tw-modal-fade-in 150ms ease-out;
}
.tw-modal:not(.hidden) > .tw-modal-surface {
	animation: tw-modal-surface-in 180ms cubic-bezier(0.2, 0.8, 0.4, 1);
}
@keyframes tw-modal-fade-in {
	from { opacity: 0; }
	to { opacity: 1; }
}
@keyframes tw-modal-surface-in {
	from { opacity: 0; transform: translateY(8px) scale(0.98); }
	to { opacity: 1; transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
	.tw-modal:not(.hidden),
	.tw-modal:not(.hidden) > .tw-modal-surface {
		animation: none;
	}
}
html.light .tw-modal { background: rgba(120, 100, 78, 0.45); }
.tw-modal-surface {
	position: relative;
	width: 100%;
	max-width: 32rem;
	background: rgb(var(--tw-bg-elevated));
	border: 1px solid rgb(var(--tw-border-default));
	border-radius: 0.75rem;
	box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35), 0 2px 6px rgba(0, 0, 0, 0.2);
	max-height: 100%;
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
html.light .tw-modal-surface {
	box-shadow: 0 10px 40px rgba(60, 40, 30, 0.18), 0 2px 6px rgba(60, 40, 30, 0.08);
}
/* `.tw-modal-surface { display: flex }` lives in styles.css (loaded after
   tailwind.css), so plain `.hidden { display: none }` ties on specificity
   and loses by source order. Toggling `.hidden` on a sibling surface (e.g.
   the feedback modal's success-state surface) had no effect, so both
   surfaces rendered side-by-side and the user saw "Thank you!" on open.
   Bumping specificity with a compound selector restores the intended hide. */
.tw-modal-surface.hidden { display: none; }
.tw-modal-surface-sm { max-width: 24rem; }
.tw-modal-surface-lg { max-width: 48rem; }
.tw-modal-surface-xl { max-width: 64rem; }
/* Optional header / body / footer sub-utilities for the surface */
.tw-modal-header {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0.875rem 1rem;
	border-bottom: 1px solid rgb(var(--tw-border-default));
}
.tw-modal-body { flex: 1 1 auto; overflow-y: auto; padding: 1rem; }
.tw-modal-footer {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	gap: 0.5rem;
	padding: 0.75rem 1rem;
	border-top: 1px solid rgb(var(--tw-border-default));
}
/* Close-X button recipe used consistently across modals */
.tw-modal-close {
	position: absolute;
	top: 0.5rem;
	inset-inline-end: 0.5rem;
	width: 2.25rem;
	height: 2.25rem;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	color: rgb(var(--tw-text-muted));
	background: transparent;
	border-radius: 0.5rem;
	transition: background 120ms, color 120ms;
	z-index: 1;
}
.tw-modal-close:hover {
	background: rgb(var(--tw-bg-hover));
	color: rgb(var(--tw-text-primary));
}

/* Return-to-dashboard + trip-footer action buttons (accent-tinted pills).
   Dark mode uses bg-accent-950/30 which washes out on cream - bump to a
   solid soft-accent in light mode for recognizable button affordance.
   NOTE: trip widgets share these classes but opt out below via the
.widget-fade-in scope - they prefer warm-neutral stone over honey. */
html.light .bg-accent-950\/30 {
	background-color: rgb(var(--tw-accent-200) / 0.55);
}
html.light .hover\:bg-accent-950\/50:hover {
	background-color: rgb(var(--tw-accent-300) / 0.55);
}
html.light .border-accent-900\/50 {
	border-color: rgb(var(--tw-accent-400) / 0.5);
}

/* Trip widgets (.widget-fade-in carousel entries): the accent palette
   reads as warm honey-yellow against cream - too loud as chrome for a
   row of info widgets. Swap shell (bg + border + hover) to warm-neutral
   stone. Icon circles inside (bg-accent-900/50) + icon colour
   (text-accent-400) keep their subtle accent tint for character. */
html.light .widget-fade-in.bg-accent-950\/30 {
	background-color: rgb(var(--tw-bg-hover));
}
html.light .widget-fade-in.bg-accent-950\/20 {
	background-color: rgb(var(--tw-bg-hover) / 0.7);
}
html.light .widget-fade-in.hover\:bg-accent-950\/40:hover,
html.light .widget-fade-in.hover\:bg-accent-950\/50:hover {
	background-color: rgb(var(--tw-bg-active));
}
html.light .widget-fade-in.border-accent-500\/50,
html.light .widget-fade-in.border-accent-500\/30 {
	border-color: rgb(var(--tw-border-default));
}
html.light .widget-fade-in.hover\:border-accent-400:hover {
	border-color: rgb(var(--tw-text-disabled));
}

/* Quick-notes widget - exception to the neutral-stone widget chrome:
   warm honey-yellow shade to signal it's the editable/interactive one
   and visually distinguish it from the info-only widgets alongside. */
html.light #notes-widget.bg-accent-950\/30 {
	background-color: rgb(var(--tw-accent-200) / 0.55);
}
html.light #notes-widget.hover\:bg-accent-950\/40:hover {
	background-color: rgb(var(--tw-accent-300) / 0.55);
}
html.light #notes-widget.border-accent-500\/50 {
	border-color: rgb(var(--tw-accent-400) / 0.5);
}
html.light #notes-widget.hover\:border-accent-400:hover {
	border-color: rgb(var(--tw-accent-500) / 0.6);
}

/* Widget typography + icon contrast in light mode. Body accent-400
   (and its hover accent-300) read too pale on stone/honey; bump deeper
   for readable copy. Icons inside the dark-rust circle (bg-accent-900
   /50) flip the other way - lift to accent-200 (pale honey) so the
   glyph punches through the dark fill. */
html.light .widget-fade-in .text-accent-400 {
	color: rgb(var(--tw-accent-700));
}
html.light .widget-fade-in .hover\:text-accent-300:hover {
	color: rgb(var(--tw-accent-600));
}
html.light .widget-fade-in .bg-accent-900\/50 .text-accent-400 {
	color: rgb(var(--tw-accent-200));
}

/* Same icon-in-dark-rust-circle treatment outside widgets: anywhere a
   bg-accent-900/50 circle contains a text-accent-400 glyph, lift the
   glyph to accent-200 in light mode so it punches through the fill
   instead of fading to a muddy same-hue blur. Covers the
   "Where's My Plane" panel icon and similar inline accent-badges. */
html.light .bg-accent-900\/50 .text-accent-400 {
	color: rgb(var(--tw-accent-200));
}

/* Luggage-risk bar-track (bg-theme-hover) collides with the new
   bg-hover widget shell - bump the track to bg-active for visible
   contrast. Generic enough to catch any future widget that uses
   bg-theme-hover as an inner wash. */
html.light .widget-fade-in .bg-theme-hover {
	background-color: rgb(var(--tw-bg-active));
}

/* Expenses modal "N to add" pill - the bg-accent-900/30 + text-accent-400
   combo loses contrast on the cream light-mode bg (faint warm wash with
   medium amber text reads as a smudge). Override to a stronger pair in
   light mode: deeper accent text on a slightly more visible accent fill. */
html.light [data-section-uncosted-badge] {
	background-color: rgb(var(--tw-accent-700) / 0.14);
	color: rgb(var(--tw-accent-700));
}

/* Event notes speech bubble - in light mode, both bg-theme-surface (panel)
   and bg-theme-elevated (bubble) resolve to the same near-white, making
   the bubble invisible against the panel. Knock the bubble to a warmer
   stone so it reads as a distinct chat-style pill. */
html.light #notes-container .bg-theme-elevated {
	background-color: rgb(var(--tw-bg-hover));
}

/* Flight event status banner - light-mode treatment.
   Per-status tinted border at reduced opacity so status identity
   stays readable without the full-saturation 500-shade punch. Fill
   softened slightly for a more calm, cream-page-blended banner. */
html.light #flight-status-banner.border-status-info-500    { border-color: rgb(var(--tw-info-500) / 0.4); }
html.light #flight-status-banner.border-status-success-500 { border-color: rgb(var(--tw-success-500) / 0.4); }
html.light #flight-status-banner.border-status-warning-500 { border-color: rgb(var(--tw-warning-500) / 0.45); }
html.light #flight-status-banner.border-status-caution-500 { border-color: rgb(var(--tw-caution-500) / 0.4); }
html.light #flight-status-banner.border-status-error-500   { border-color: rgb(var(--tw-error-500) / 0.4); }
html.light #flight-status-banner.border-status-alert-500   { border-color: rgb(var(--tw-alert-500) / 0.4); }
html.light #flight-status-banner.bg-status-info-900    { background-color: rgb(var(--tw-info-900) / 0.6); }
html.light #flight-status-banner.bg-status-success-900 { background-color: rgb(var(--tw-success-900) / 0.6); }
html.light #flight-status-banner.bg-status-warning-900 { background-color: rgb(var(--tw-warning-900) / 0.6); }
html.light #flight-status-banner.bg-status-caution-900 { background-color: rgb(var(--tw-caution-900) / 0.6); }
html.light #flight-status-banner.bg-status-error-900   { background-color: rgb(var(--tw-error-900) / 0.6); }
html.light #flight-status-banner.bg-status-alert-900   { background-color: rgb(var(--tw-alert-900) / 0.6); }

/* Flight hero progress bar — track + fill flip per theme so it doesn't
   read as a screaming-white slab against the teal En Route banner in
   dark mode. Dark mode: deep info-900 teal. Light mode: white.
   Tokens defined inline (not via Tailwind classes) because both pieces
   are sub-1px detail and the colour rules don't generalise. */
#hero-progress-bar-track { background-color: rgb(var(--tw-info-900) / 0.5); }
#hero-progress-bar       { background-color: rgb(var(--tw-info-900)); }
html.light #hero-progress-bar-track { background-color: rgb(255 255 255 / 0.35); }
html.light #hero-progress-bar       { background-color: rgb(255 255 255); }

/* Light-mode override for the JS-applied copy-success state in app.js
   (twInitializeCopyConfirmationButton). The dark-mode pairing of
   bg-status-success-900/50 + text-status-success-200 inverts in light mode
   and reads as "dark green plate with light text" against an otherwise
   light page; flip to a low-opacity success tint with a saturated check
   icon so the cue still reads success but matches the rest of light. */
html.light .tw-copy-success.bg-status-success-900\/50 { background-color: rgb(var(--tw-success-500) / 0.15); }
html.light .tw-copy-success.border-status-success-500 { border-color: rgb(var(--tw-success-500) / 0.4); }
html.light .tw-copy-success .text-status-success-200  { color: rgb(var(--tw-success-500)); }

/* Hero-card delay time text (`text-status-warning` shorthand = warning-400).
   Light-mode warning-400 renders as muddy mustard-brown; map the
   shorthand to accent-600 (warm rust-orange) for a clear "slight
   delay" signal on cream. */
html.light .text-status-warning {
	color: rgb(var(--tw-accent-600));
}

/* Packing-list widget "Mild" bar + any bg-status-warning-500 fill
   reads as muddy mustard-brown in light mode (warning-500 = 174 124
   32). Shift to a proper amber so "mild" / warning tiers punch
   correctly on cream. Scoped bg-only so text-status-warning-500
   (unusual) is untouched and the shorthand above continues to win. */
html.light .bg-status-warning-500 {
	background-color: rgb(210 148 28);
}

/* Warning-banner background fill `bg-status-warning-900/30` (used by
   the private-relay warning on /dashboard, the "Pending" / online-
   check-in info banners on /design, and flight detail warning blocks)
   reads near-invisible in light mode: warning-900 = 248 234 196 on a
   cream page with 0.3 alpha = essentially no tint. Boost the alpha so
   the banner reads as a distinct surface (mirrors the targeted
   #flight-status-banner override above). Scoped to class[*/30] so
   the un-alpha'd `bg-status-warning-900` (used by pill backgrounds
   that intentionally render as a deep mustard chip) isn't touched. */
html.light [class*="bg-status-warning-900\/30"] {
	background-color: rgb(var(--tw-warning-500) / 0.18);
}

/* CrimeSafe / location score gauge background track - theme-aware.
   SVG stroke attribute was hardcoded #675641 (warm-brown gray); swap to
   a CSS class so the track lightens on cream bg. */
.tw-gauge-track { stroke: rgb(var(--tw-bg-hover)); }

/* Expenses widget donut: empty-track ring that appears behind the colored
   per-category arcs. Class form (vs an inline stroke attribute) so the
   var() resolves reliably across browsers - some treat var() inside SVG
   presentation attributes as a literal string. */
.tw-expenses-donut-track { stroke: rgb(var(--tw-bg-hover)); stroke-opacity: 0.4; }

/* Accom-event + booking-details "Helpful Tips" heading - text-accent-400
   renders too light in light mode (medium amber on pale cream). Bump to
   the darker accent-700 in light mode for readable heading contrast. */
html.light .bg-accent-900\/20 .text-accent-400,
html.light .bg-accent-900\/20 > .flex > span.text-accent-400 {
	color: rgb(var(--tw-accent-700));
}
/* Same washout in the expenses modal "N to add" pill (bg-accent-900/30 +
   text-accent-400). Mirror the /20 rule. */
html.light .bg-accent-900\/30.text-accent-400,
html.light span.bg-accent-900\/30.text-accent-400 {
	color: rgb(var(--tw-accent-700));
}
/* Bottom trip-widgets (Share / Dashboard / Feedback) - the accent-700/20
   icon ring + text-accent-400 icon both flatten into a pale warm wash on
   the cream widget bg in light mode, leaving the icon badge near-invisible.
   Bump the ring alpha and darken the icon so the circular badge reads. */
html.light .bg-accent-700\/20 {
	background-color: rgb(var(--tw-accent-700) / 0.22);
}
html.light .group:hover .group-hover\:bg-accent-700\/30 {
	background-color: rgb(var(--tw-accent-700) / 0.35);
}
html.light .bg-accent-700\/20 .text-accent-400,
html.light .bg-accent-700\/20 > .text-accent-400,
html.light .bg-accent-700\/20 > svg.text-accent-400 {
	color: rgb(var(--tw-accent-700));
}

/* Booking-details "Breakfast included" pill - bg-accent-900/30 + text-accent-300
   + border-accent-700/50 reads as faded amber-text-on-peach in light mode (text
   barely distinguishable from pill bg). Bump fill toward visible accent-700 wash,
   shift text + icon to dark accent for clean contrast on cream. Border bumped to
   match. The same combo on the icon (text-accent-300) inherits via currentColor
   on the SVG. */
html.light [data-breakfast-pill="yes"] {
	background-color: rgb(var(--tw-accent-700) / 0.16);
	color: rgb(var(--tw-accent-700));
	border-color: rgb(var(--tw-accent-700) / 0.35);
}

/* Next upcoming event highlight - outer ring breathing.
   1px shadow ring at trough, brighter ring + soft outer halo at peak. No
   layout shift since base border on the card is untouched.
   Uses --tw-yellow-500 (same as today's day border). */
.tw-next-event {
	animation: tw-next-event-pulse 3.2s ease-in-out infinite;
}
@keyframes tw-next-event-pulse {
	0%, 100% { box-shadow: 0 0 0 1px rgb(var(--tw-yellow-500) / 0.20); }
	50%      { box-shadow: 0 0 0 1px rgb(var(--tw-yellow-500) / 0.65), 0 0 12px rgb(var(--tw-yellow-500) / 0.20); }
}
.tw-next-event .tw-countdown-badge {
	color: rgb(var(--tw-yellow-500)) !important;
	background: rgb(var(--tw-yellow-500) / 0.12) !important;
}

/* Active flight-status pill breathe. Applied by tw_flight_action_pill() to
   any non-terminal phase (TRACKING, BOARDING, TAXIING, IN FLIGHT, LANDED,
   DELAYED, DIVERTED) so the user can tell the flight is still in motion at
   a glance. Final/inert states (DEPART, ARRIVE, ARRIVED, CANCELLED) hold
   steady. 1.4s cadence reads as alive without nagging. Brightness filter
   (vs opacity) keeps the pill at full legibility through the cycle - the
   tone just lifts and settles. */
.tw-pill-pulse {
	animation: tw-pill-pulse 1s ease-in-out infinite;
}
@keyframes tw-pill-pulse {
	0%, 100% { filter: brightness(0.9); }
	50%      { filter: brightness(1.1); }
}
/* Slower variant for TRACKING. Pre-flight tracking is steady-state - we have
   eyes on the flight but nothing's actively happening - so a calm breathe
   reads as "watching, not waiting". The faster 1s pulse is reserved for
   active phases (boarding, taxiing, in flight, landed, delayed, diverted)
   where the user might need to act. */
.tw-pill-pulse-slow {
	animation: tw-pill-pulse 2s ease-in-out infinite;
}

/* Boarding pill (accent-yellow tone) light-mode override. Default tokens
   give pale-yellow text (accent-200) on rust-orange-tinted bg (accent-500),
   which is unreadable in light mode. Swap to dark text (accent-700) on a
   lighter pale-yellow bg (accent-200) so it reads. Trip-page pill,
   event-page badge, and event-page hero banner all covered. */
html.light .bg-accent-500\/20.text-accent-200,
html.light .bg-accent-900.text-accent-200,
html.light .bg-accent-900\/75.text-accent-200 {
	background-color: rgb(var(--tw-accent-200) / 0.6);
	color: rgb(var(--tw-accent-700));
	border-color: rgb(var(--tw-accent-500) / 0.5);
}


/* ============================================
   TOUCH FEEDBACK
   Native-feeling tap responses for mobile
   ============================================ */

/* Disable default blue/gray tap highlight */
* { -webkit-tap-highlight-color: transparent; }

/* Smooth transitions for interactive elements */
a[href] { transition: transform 120ms ease-out; }

/*
* CSS :active only on non-touch devices (mouse/trackpad).
* On touch, :active fires on touchstart before the browser knows if the
* user intends to scroll - causing false visual feedback on iOS WebView.
* iOS WebView falsely reports (hover:hover) and (pointer:fine), so we
* use a JS-set .touch-device class on <html> instead of media queries.
* Touch feedback is handled by JS .tw-tap-* classes (see app.js).
* Detection: header.php adds .touch-device to <html> before CSS loads.
*/
html:not(.touch-device) a[href]:active {
	transform: scale(0.97);
	transition: transform 60ms ease-out;
}

html:not(.touch-device) button:active,
html:not(.touch-device) [type="submit"]:active {
	transform: scale(0.95);
	filter: brightness(0.85);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}

html:not(.touch-device) [data-trip-accordion-target]:active {
	transform: none;
	filter: none;
}

html:not(.touch-device) #tw-bottom-nav a:active,
html:not(.touch-device) #tw-bottom-nav button:active {
	transform: scale(0.92);
	box-shadow: 0 0 0 3px rgb(var(--tw-accent-700) / 0.4);
}

html:not(.touch-device) .grid a[href^="/trip/"]:hover {
	filter: brightness(1.3);
}
html:not(.touch-device) .grid a[href^="/trip/"]:active {
	transform: scale(0.98);
	filter: brightness(1.15);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}

html:not(.touch-device) [data-trip-accordion] a[href^="/event/"]:active {
	transform: scale(0.98);
	filter: brightness(0.9);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}

/*
* Tailwind hover: classes are globally gated via tailwind.config.js plugin:
*   addVariant('hover', 'html:not(.touch-device) &:hover')
* This prevents ALL hover visual changes on touch devices (iOS sticky hover).
* Touch feedback is provided by JS .tw-tap-* classes on genuine taps only.
*/

/* Touch feedback - JS-controlled class, only added on genuine taps (not scroll) */
.tw-tap-link {
	transform: scale(0.97);
	transition: transform 60ms ease-out;
}
.tw-tap-button {
	transform: scale(0.95);
	filter: brightness(0.85);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}
.tw-tap-nav {
	transform: scale(0.92);
	box-shadow: 0 0 0 3px rgb(var(--tw-accent-700) / 0.4);
}
.grid a[href^="/trip/"].tw-tap-card {
	transform: scale(0.98);
	filter: brightness(1.15);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}
[data-trip-accordion] a[href^="/event/"].tw-tap-card {
	transform: scale(0.98);
	filter: brightness(0.9);
	transition: transform 60ms ease-out, filter 60ms ease-out;
}

/* Reduced motion: no transforms */
@media (prefers-reduced-motion: reduce) {
.tw-tap-link, .tw-tap-button, .tw-tap-nav, .tw-tap-card {
		transform: none !important;
		opacity: 0.7;
	}
}

/* ============================================
   BASE STYLES
   ============================================ */

/* Base html overrides */
html {
	overflow-x: hidden;
	scroll-behavior: smooth;
	-webkit-tap-highlight-color: transparent;
}

/* Suppress focus outline on mouse/touch click, keep for keyboard (Tab) navigation */
:focus:not(:focus-visible) {
	outline: none;
	box-shadow: none;
}

/* Content dim on navigation - instant feedback while page loads.
   Transition smooths the 1.0 → 0.5 dim on visit:start AND the 0.5 → 1.0
   un-dim on content:replace; previously both were hard cuts. */
main {
	transition: opacity var(--tw-transition-fast);
}
/* Top-pad main to clear the (now taller) fixed #tw-header - SCOPED to the
   beta iOS shell only. 4rem (Tailwind pt-16 on <main>) covers the header's
   base chrome; the safe-area offset mirrors the iOS-only header padding
   above so content sits flush under the header without overlap. WTN iOS
   and Safari users are untouched. */
html.tw-ios main#swup {
	padding-top: calc(4rem + max(0px, calc(env(safe-area-inset-top, 0px) - 8px)));
}
main.tw-navigating {
	opacity: 0.5;
	pointer-events: none;
}
/* Back-navigation entry fade removed 2026-05-18. Was added when first-back
   was a real fetch that masked the 140ms opacity-0->1 animation; with the
   visit:start live-DOM snapshot, the swap is instant and the animation
   plays cleanly over identical content as a visible flash-to-invisible
   ("stuck dim half the time"). The snapshot itself is the arrival cue. */
@media (prefers-reduced-motion: reduce) {
	main { transition: none; }
}

/* Heading font application now handled at the top of this file (h1 + h2:not(.uppercase)
   pick up Baloo 2 via the theme system). The old Sanchez block also applied a
   -webkit-text-stroke fake-bold workaround which Baloo 2 doesn't need (it ships
   proper 500/600/700/800 weights). */

/* ============================================
   DATEPICKER STYLING
   ============================================ */

/* Datepicker selected date highlighting */
.datepicker-cell.selected,
.datepicker-cell.selected:hover,
[date-rangepicker] .datepicker-cell.selected,
[date-rangepicker] .datepicker-cell.selected:hover {
	background-color: rgb(var(--tw-accent-600)) !important; /* accent-600 */
	color: white !important;
}

.datepicker-cell.range-start,
.datepicker-cell.range-end,
[date-rangepicker] .datepicker-cell.range-start,
[date-rangepicker] .datepicker-cell.range-end {
	background-color: rgb(var(--tw-accent-600)) !important; /* accent-600 */
	color: white !important;
}

.datepicker-cell.range,
[date-rangepicker] .datepicker-cell.range {
	background-color: rgb(var(--tw-accent-500)) !important; /* accent-500 */
	color: white !important;
}

.datepicker-cell.focused:not(.selected),
[date-rangepicker] .datepicker-cell.focused:not(.selected) {
	background-color: rgb(var(--tw-bg-hover)) !important; /* theme hover */
}

/* Datepicker popup styling - only style the outer container */
.datepicker-dropdown,
.datepicker.datepicker-dropdown {
	background-color: rgb(var(--tw-bg-elevated)) !important; /* theme elevated */
	border: 1px solid rgb(var(--tw-border-default)) !important; /* theme border */
	box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3) !important;
	border-radius: 0.5rem !important;
	padding: 0.75rem !important;
}

/* Remove duplicate styling from inner picker */
.datepicker-picker {
	background-color: transparent !important;
	border: none !important;
	box-shadow: none !important;
	padding: 0 !important;
}

.datepicker-view {
	padding: 0 !important;
	margin: 0 !important;
	background-color: transparent !important;
}

.datepicker-grid {
	background-color: transparent !important;
	padding: 0 !important;
	margin: 0 !important;
}

.datepicker-main {
	background-color: transparent !important;
	border: none !important;
	padding: 0 !important;
}

.datepicker-cell {
	background-color: transparent !important;
	border: none !important;
	color: rgb(var(--tw-text-primary)) !important;
}

.datepicker-header {
	margin-bottom: 0.5rem !important;
	padding: 0 !important;
}

.datepicker-controls {
	padding: 0 !important;
	margin-bottom: 0.5rem !important;
}

/* Navigation arrows */
.datepicker-controls .button,
.datepicker-controls button,
.datepicker .prev-btn,
.datepicker .next-btn {
	color: rgb(var(--tw-text-primary)) !important;
	background-color: transparent !important;
}

.datepicker-controls .button:hover,
.datepicker-controls button:hover,
.datepicker .prev-btn:hover,
.datepicker .next-btn:hover {
	background-color: rgb(var(--tw-bg-hover)) !important; /* theme hover */
}

/* Arrow SVG icons - use currentColor so they track the button text in both modes */
.datepicker-controls svg,
.datepicker .prev-btn svg,
.datepicker .next-btn svg {
	stroke: currentColor !important;
	fill: currentColor !important;
}

/* Month/year title */
.datepicker-view .view-switch,
.datepicker .view-switch,
.datepicker-title,
.datepicker-controls .view-switch {
	color: rgb(var(--tw-text-primary)) !important;
	font-weight: 600 !important;
}

/* Day of week headers */
.datepicker-view .dow,
.datepicker .dow,
.datepicker .days-of-week .dow {
	color: rgb(var(--tw-text-secondary)) !important; /* theme text secondary */
}

/* Day cells */
.datepicker-cell.day {
	color: rgb(var(--tw-text-primary)) !important;
}

/* Previous/next month dates */
.datepicker-cell.prev,
.datepicker-cell.next {
	color: rgb(var(--tw-text-muted)) !important; /* theme text muted */
}

/* Disabled dates */
.datepicker-cell.disabled {
	color: rgb(var(--tw-text-disabled)) !important; /* theme text disabled */
}

/* ============================================
   CUSTOM TIME PICKER
   ============================================ */

/* Icon button (clock) inside the input wrapper */
.tw-tp-icon {
	position: absolute;
	right: 0.625rem;
	top: 50%;
	transform: translateY(-50%) !important;
	color: rgb(var(--tw-text-muted));
	cursor: pointer;
	padding: 0.25rem;
	border: none;
	background: none;
	z-index: 1;
	line-height: 0;
}
html:not(.touch-device) .tw-tp-icon:hover {
	color: rgb(var(--tw-text-secondary));
}
.tw-tp-icon:active {
	transform: translateY(-50%) !important;
}

/* Give the input right padding for the icon */
input[data-time-input] {
	padding-right: 2.5rem !important;
}

/* Dropdown container */
.tw-tp-dropdown {
	display: none;
	position: absolute;
	left: 0;
	right: 0;
	top: 100%;
	margin-top: 0.25rem;
	z-index: 50;
	background: rgb(var(--tw-bg-elevated));
	border: 1px solid rgb(var(--tw-border-default));
	border-radius: 0.5rem;
	box-shadow: 0 10px 15px -3px rgba(0,0,0,.3), 0 4px 6px -4px rgba(0,0,0,.2);
	overflow: hidden;
}
.tw-tp-dropdown.tw-tp-above {
	top: auto;
	bottom: 100%;
	margin-top: 0;
	margin-bottom: 0.25rem;
}
.tw-tp-dropdown.is-open {
	display: block;
}

/* Scroll columns */
.tw-tp-scroll {
	height: 220px; /* 5 × 44px */
	overflow-y: auto;
	scroll-snap-type: y mandatory;
	overscroll-behavior: contain;
	flex: 1;
	display: flex;
	flex-direction: column;
}
.tw-tp-scroll::-webkit-scrollbar { display: none; }
.tw-tp-scroll { scrollbar-width: none; }

/* Individual items */
.tw-tp-item {
	height: 44px;
	min-height: 44px;
	line-height: 44px;
	text-align: center;
	scroll-snap-align: center;
	font-size: 1.125rem;
	color: rgb(var(--tw-text-muted));
	cursor: pointer;
	user-select: none;
	flex-shrink: 0;
	transition: color 0.15s;
}
html:not(.touch-device) .tw-tp-item:hover {
	color: rgb(var(--tw-text-primary));
}
.tw-tp-item.is-selected {
	color: rgb(var(--tw-text-primary));
	font-weight: 600;
}

/* Center highlight band */
.tw-tp-highlight {
	position: absolute;
	left: 0;
	right: 0;
	top: 88px; /* 2 × 44px */
	height: 44px;
	border-top: 1px solid rgb(var(--tw-border-default));
	border-bottom: 1px solid rgb(var(--tw-border-default));
	pointer-events: none;
	background: rgb(var(--tw-accent-700) / 0.08);
}

/* ============================================
   DATE INPUT STYLING
   ============================================ */

/* Native date input styling - matches time input pattern */
input[type="date"] {
	cursor: text !important;
	pointer-events: auto !important;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%23A4956B' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.625rem center;
	background-size: 1rem;
	padding-right: 2.5rem !important;
	position: relative;
}

input[type="date"]::-webkit-calendar-picker-indicator {
	cursor: pointer !important;
	opacity: 0;
	position: absolute;
	right: 0;
	width: 2.5rem;
	height: 100%;
	top: 0;
}

input[type="date"]:hover {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%23D3CDB5' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z'/%3E%3C/svg%3E");
}

/* Select chevron - override Flowbite's grey-blue default to match calendar icon accent */
select:not([size]) {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%23A4956B' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
}

select:not([size]):hover {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%23D3CDB5' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
}

/* Light-mode overrides - SVG stroke must be hardcoded in data-URL; swap to
   darker stone hex so icons have contrast on cream surfaces. Matches the
   --tw-text-muted (7C736A) / --tw-text-secondary (4A423A) values. */
html.light input[type="date"] {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%237C736A' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z'/%3E%3C/svg%3E");
}
html.light input[type="date"]:hover {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' viewBox='0 0 24 24'%3E%3Cpath stroke='%234A423A' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 10h16m-8-3V4M7 7V4m10 3V4M5 20h14a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1Z'/%3E%3C/svg%3E");
}
html.light select:not([size]) {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%237C736A' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
}
html.light select:not([size]):hover {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 10 6'%3E%3Cpath stroke='%234A423A' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m1 1 4 4 4-4'/%3E%3C/svg%3E");
}

/* ============================================
   BORDER RADIUS UTILITIES
   (from Flowbite datepicker - used for cards/accordions)
   ============================================ */

.rounded-base {
	border-radius: var(--radius-base);
}

.rounded-s-base {
	border-start-start-radius: var(--radius-base);
	border-end-start-radius: var(--radius-base);
}

.rounded-e-base {
	border-start-end-radius: var(--radius-base);
	border-end-end-radius: var(--radius-base);
}

/* ============================================
   HEADER COMPONENTS
   ============================================ */

header .avatar {
	width: 2em;
	height: 2em;
	border-radius: 4em;
}

/* ============================================
   TIMELINE COMPONENTS
   ============================================ */

.timeline-item {
	margin-left: 1em;
}

.timeline-item .timeline-image {
	text-align: right;
}

.timeline-item .timeline-image img {
	border-radius: 10em;
	width: 5em;
	height: 5em;
	background-color: #fff;
}

.timeline-item .timeline-icon svg {
	width: 3em;
	height: 3em;
	margin: 1em 0 1em 0;
}

.timeline-item .timeline-time {
	font-size: 1.4em;
}

.timeline-item .flight-flags img {
	width: 1em;
	height: 1em;
}

/* ============================================
   TRIP ACCORDION SCROLL OFFSET
   ============================================ */

/* Offset scroll position to account for fixed navbar */
[id^="accordion-day-"] {
	scroll-margin-top: 80px; /* navbar height (~72px) + padding */
}

/* Toast notification for "jumping to today" indicator */
.tw-collapse-toast {
	position: fixed;
	bottom: 1.5rem;
	left: 50%;
	transform: translateX(-50%);
	background-color: rgb(var(--tw-bg-elevated));
	border: 1px solid rgb(var(--tw-border-default));
	color: rgb(var(--tw-text-secondary));
	padding: 0.5rem 1rem;
	border-radius: 9999px;
	font-size: 0.875rem;
	box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
	z-index: 50;
	opacity: 0;
	transition: opacity 0.3s ease-in-out;
}

.tw-collapse-toast.visible {
	opacity: 1;
}

/* Reduce motion preference - disable smooth scrolling and animations */
@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}

	[id^="accordion-day-"] [data-trip-accordion-target] + div {
		transition: none !important;
	}

.tw-collapse-toast {
		transition: none;
	}
}

/* ============================================
   STAGGERED ENTRANCE ANIMATIONS
   ============================================ */

@keyframes stagger-fade-up {
	from { opacity: 0; transform: translateY(12px); }
	to { opacity: 1; transform: translateY(0); }
}

/* Dashboard trip cards. Duration unified at 250ms across dashboard, trip
   day accordions, and event sections so the entry feel is consistent
   regardless of which surface the user lands on. */
.grid > a[href^="/trip/"] {
	animation: stagger-fade-up var(--tw-transition-reveal) both;
}
.grid > a[href^="/trip/"]:nth-child(1) { animation-delay: 0ms; }
.grid > a[href^="/trip/"]:nth-child(2) { animation-delay: 50ms; }
.grid > a[href^="/trip/"]:nth-child(3) { animation-delay: 100ms; }
.grid > a[href^="/trip/"]:nth-child(4) { animation-delay: 150ms; }
.grid > a[href^="/trip/"]:nth-child(5) { animation-delay: 200ms; }
.grid > a[href^="/trip/"]:nth-child(n+6) { animation-delay: 250ms; }

/* Trip day accordions */
[data-trip-accordion] {
	animation: stagger-fade-up var(--tw-transition-reveal) both;
}
/* Owner-only stutter experiment: skip painting off-screen day accordions.
   Layout still runs so anchor jumps and getBoundingClientRect stay
   correct; only paint is skipped. The .is-offscreen class is toggled by
   IntersectionObserver in app.js (see twInitDayAccordionViewportCull). */
[data-vis-cull] [data-trip-accordion].is-offscreen {
	visibility: hidden;
}
/* Each event card LI is independent for layout (sibling cards never affect
   one another's box) and for style inheritance (no descendant inherits
   from a sibling). Telling the engine that with `contain` lets it skip
   re-layout / re-style sweeps for off-viewport cards on long trips, which
   is the dominant scroll-stutter cost on Android WebView.
   Deliberately NO `paint`: the timeline icon span at trip/_event_card.php:98
   uses `-start-11` to hang -2.75rem outside the LI's box; paint
   containment would clip it and the bullet icons would vanish. */
[data-trip-accordion] ol > li {
	contain: layout style;
}
[data-trip-accordion]:nth-child(1) { animation-delay: 0ms; }
[data-trip-accordion]:nth-child(2) { animation-delay: 40ms; }
[data-trip-accordion]:nth-child(3) { animation-delay: 80ms; }
[data-trip-accordion]:nth-child(4) { animation-delay: 120ms; }
[data-trip-accordion]:nth-child(5) { animation-delay: 160ms; }
[data-trip-accordion]:nth-child(n+6) { animation-delay: 200ms; }

[data-trip-accordion="collapse"] > h2 > button {
	background-color: rgb(var(--tw-bg-active));
}
/* Event page sections */
.event-section {
	animation: stagger-fade-up var(--tw-transition-reveal) both;
}
.event-section:nth-child(1) { animation-delay: 0ms; }
.event-section:nth-child(2) { animation-delay: 60ms; }
.event-section:nth-child(3) { animation-delay: 120ms; }
.event-section:nth-child(4) { animation-delay: 180ms; }
.event-section:nth-child(5) { animation-delay: 240ms; }
.event-section:nth-child(n+6) { animation-delay: 300ms; }

/* Suppress all entrance animations/transitions on back navigation */
html[data-vt-back] .grid > a[href^="/trip/"],
html[data-vt-back] [data-trip-accordion],
html[data-vt-back] .event-section {
	animation: none;
}
.widget-fade-in {
	box-shadow: 0 0 10px rgba(180, 140, 40, 0.15);
}
/* Skip fade-in transitions on back navigation - content should appear instantly */
html[data-vt-back] .widget-fade-in {
	opacity: 1 !important;
	transition: none !important;
}
html[data-vt-back] .img-fade-in {
	transition: none !important;
}

/* Suppress animations + transitions during trip init (layout-first, animate-after) */
html[data-trip-init="pending"] [data-trip-accordion] {
	animation: none !important;
	transition: none !important;
}
html[data-trip-init="pending"] [data-trip-accordion] [id^="collapse-"] {
	transition: none !important;
}
/* On back navigation, hide trip content until init pipeline completes (prevents flash of
   all-expanded accordions before JS collapses past days and restores scroll position).
   Only applies when both flags are present - forward loads show content immediately. */
html[data-vt-back][data-trip-init="pending"] #trip-content {
	visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
.grid > a[href^="/trip/"],
	[data-trip-accordion],
.event-section {
		animation: none;
	}
}

/* ============================================
   LAYOUT STABILITY
   Reserve space for async content to prevent layout jumps
   ============================================ */

/* Reserve space for delayed map iframes */
.tw-delayed-map {
	aspect-ratio: 16 / 9;
	width: 100%;
	background-color: rgb(var(--tw-bg-elevated));
	border-radius: var(--radius-base);
}

/* Async-revealed pills: drive-time pills under events, day-weather badges
   in accordion headers, and the lazy carousel widgets that the trip-weather
   API fills in (temperature range, packing distribution).
   The :not(...) gates are critical - declaring the animation on the
   always-on selector lets it play out while the element is still invisible,
   leaving the user with an instant pop-in once JS strips the hidden /
   invisible class. With the gate, the animation only triggers in the same
   frame the element becomes visible. */
[data-drive-time-placeholder]:not(.invisible),
.day-weather:not(.hidden),
#weather-temp-widget:not(.hidden),
#weather-packing-widget:not(.hidden) {
	animation: pill-reveal var(--tw-transition-reveal);
}
@keyframes pill-reveal {
	from { opacity: 0; transform: translateY(-4px); }
	to { opacity: 1; transform: translateY(0); }
}
html[data-vt-back] [data-drive-time-placeholder]:not(.invisible),
html[data-vt-back] .day-weather:not(.hidden),
html[data-vt-back] #weather-temp-widget:not(.hidden),
html[data-vt-back] #weather-packing-widget:not(.hidden) {
	animation: none;
}
@media (prefers-reduced-motion: reduce) {
	[data-drive-time-placeholder]:not(.invisible),
	.day-weather:not(.hidden),
	#weather-temp-widget:not(.hidden),
	#weather-packing-widget:not(.hidden) {
		animation: none;
	}
}

/* ============================================
   MAP COMPONENTS
   ============================================ */

.leaflet-container {
	background-color: rgba(0, 0, 0, 0.8);
}

/* ============================================
   SWUP PROGRESS BAR
   ============================================ */

.tw-progress-bar {
	background: linear-gradient(90deg,
		rgb(var(--tw-accent-700)),
		rgb(var(--tw-accent-500)),
		rgb(var(--tw-accent-400))
	) !important;
	overflow: visible !important;
}

.tw-progress-bar::after {
	content: '';
	position: absolute;
	right: -3px;
	top: 0;
	width: 6px;
	height: 3px;
	background: rgb(var(--tw-accent-400));
	border-radius: 0 3px 3px 0;
	transform: scaleX(calc(1 / var(--progress, 1)));
	transform-origin: left;
}

/* ============================================
   BOTTOM NAVIGATION
   ============================================ */

/* Safe area for iOS devices with home indicator.
   Prefers WTN-provided inset (set via --tw-safe-bottom), falls back to env()
   for plain web / any context where the native bridge didn't populate the var. */
.safe-area-bottom {
	padding-bottom: var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px));
}

/* Leaflet maps: isolate z-index stacking so controls don't overlap header/nav */
#tw-live-track-map,
#flight-map {
	position: relative;
	z-index: 0;
	isolation: isolate;
}

/* Header: scroll hide */
#tw-header {
	transition: transform var(--tw-transition-slow);
}
/* Notch / Dynamic Island padding - SCOPED to the beta iOS shell only so
   the live WTN iOS app and plain iPhone Safari are unaffected. Our beta
   shell sets html.tw-ios at document-start; WTN does not. -8px tightens
   the visual gap between the notch and the header content (env() alone
   stacks under py-2 and looks bottom-heavy). max(0px) keeps non-negative. */
html.tw-ios #tw-header {
	padding-top: max(0px, calc(env(safe-area-inset-top, 0px) - 8px));
}
#tw-header.tw-nav-fast {
	transition: transform var(--tw-transition-standard);
}
/* Suppress transform animation during programmatic scroll restoration so the
   header doesn't drift up/down while twRestoreScrollAnchor() is mid-correction.
   Toggled by twRestoreScrollAnchor / twScheduleScrollCorrection in app.js. */
#tw-header.tw-nav-suppress,
#tw-bottom-nav.tw-nav-suppress {
	transition: none !important;
}
#tw-header.tw-header-hidden {
	transform: translateY(-100%);
}

/* Bottom nav: centering, safe area, scroll hide */
#tw-bottom-nav {
	left: 50%;
	transform: translateX(-50%);
	padding-bottom: var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px));
	transition: transform var(--tw-transition-slow);
}
/* Beta iOS shell only: pull the nav buttons closer to the bottom edge
   than the full home indicator inset (~34px) would put them. -25px
   settles the buttons just above the swipe-gesture zone. WTN iOS and
   web Safari keep the original behaviour. */
html.tw-ios #tw-bottom-nav {
	padding-bottom: max(0px, calc(env(safe-area-inset-bottom, 0px) - 25px));
}
#tw-bottom-nav.tw-nav-fast {
	transition: transform var(--tw-transition-standard);
}

/* Hide bottom nav (JS-toggled) */
#tw-bottom-nav.tw-nav-hidden {
	transform: translateX(-50%) translateY(100%);
}

/* Scroll offset to account for sticky header */
html {
	scroll-padding-top: 60px;
}

/* Add event dropdown animation */
#add-event-dropdown {
	opacity: 0;
	transform: translateY(10px);
	transition: opacity var(--tw-transition-standard), transform var(--tw-transition-standard);
	pointer-events: none;
}

#add-event-dropdown.show {
	opacity: 1;
	transform: translateY(0);
	pointer-events: auto;
}


/* Backdrop blur for transparent nav/bottom bar */
.backdrop-blur-sm {
	-webkit-backdrop-filter: blur(8px);
	backdrop-filter: blur(8px);
}

/* ============================================
   HEADER LOGO/BACK BUTTON TRANSITIONS
   ============================================ */

/* Smooth transitions for header elements when JS toggles visibility */
.header-logo,
.header-back-btn {
	transition: opacity var(--tw-transition-standard), transform var(--tw-transition-standard);
}

/* Trip page logo wiggle - draws attention to trip nav dropdown */
.header-logo.logo-bounce > img,
img.logo-bounce {
	animation: logo-wiggle 600ms ease-in-out 600ms both;
}
@keyframes logo-wiggle {
	0%   { transform: rotate(0deg); }
	20%  { transform: rotate(6deg); }
	40%  { transform: rotate(-4.5deg); }
	60%  { transform: rotate(3deg); }
	80%  { transform: rotate(-1.5deg); }
	100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
.header-logo.logo-bounce > img,
	img.logo-bounce { animation: none; }
}

/* ============================================
   BOTTOM NAVIGATION TRANSITIONS
   ============================================ */

/* Bottom nav inner container needs relative positioning */
#tw-bottom-nav > div {
	position: relative;
}

/* Bottom nav sections - hidden ones absolute so only the visible one sizes the pill */
#tw-nav-dashboard,
#tw-nav-trip,
#tw-nav-event,
#tw-nav-back {
	transition: opacity var(--tw-transition-standard);
}

/* Hidden state for nav sections - absolute so they don't affect size */
#tw-nav-dashboard[data-visible="false"],
#tw-nav-trip[data-visible="false"],
#tw-nav-event[data-visible="false"],
#tw-nav-back[data-visible="false"] {
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	transform: translateY(-50%);
	opacity: 0;
	pointer-events: none;
}

/* Visible state - static position so it drives container size */
#tw-nav-dashboard[data-visible="true"],
#tw-nav-trip[data-visible="true"],
#tw-nav-event[data-visible="true"],
#tw-nav-back[data-visible="true"] {
	animation: nav-enter 200ms ease-out;
}
@keyframes nav-enter {
	from { opacity: 0; transform: translateY(8px); }
	to { opacity: 1; transform: translateY(0); }
}

/* Reduce motion preference */
@media (prefers-reduced-motion: reduce) {
	#tw-header,
	#add-event-dropdown,
.header-logo,
.header-back-btn,
	#tw-nav-dashboard,
	#tw-nav-trip,
	#tw-nav-event,
	#tw-nav-back {
		transition: none;
	}
	#tw-nav-dashboard[data-visible="true"],
	#tw-nav-trip[data-visible="true"],
	#tw-nav-event[data-visible="true"],
	#tw-nav-back[data-visible="true"] {
		animation: none;
	}
}

/* ============================================
   PWA INSTALL PROMPT
   ============================================ */

/* Slide-up animation for install banner */
.pwa-slide-up {
	animation: pwaSlideUp 0.3s ease-out forwards;
}

@keyframes pwaSlideUp {
	from {
		opacity: 0;
		transform: translateY(20px);
	}
	to {
		opacity: 1;
		transform: translateY(0);
	}
}

/* Auto-hide install prompts when running as installed PWA */
@media (display-mode: standalone) {
.pwa-install-prompt {
		display: none !important;
	}
}

/* Reduce motion preference for PWA prompts */
@media (prefers-reduced-motion: reduce) {
.pwa-slide-up {
		animation: none;
	}
}

/* ============================================
   TOGGLE SWITCH COMPONENT
   Custom toggle for settings pages
   ============================================ */
.toggle-track {
	position: relative;
	width: 2.75rem;     /* w-11 */
	height: 1.5rem;     /* h-6 */
	background: rgb(var(--tw-bg-hover));
	border-radius: 9999px;
	transition: background-color 0.2s;
}

.toggle-track::after {
	content: '';
	position: absolute;
	top: 2px;
	left: 2px;
	width: 1.25rem;     /* w-5 */
	height: 1.25rem;    /* h-5 */
	background: white;
	border-radius: 9999px;
	transition: transform 0.2s;
}

/* Checked state - controlled by peer checkbox */
.peer:checked ~ .toggle-track {
	background: rgb(var(--tw-accent-600));
}

.peer:checked ~ .toggle-track::after {
	transform: translateX(1.25rem);
}

/* Focus state */
.peer:focus ~ .toggle-track {
	outline: none;
	box-shadow: 0 0 0 4px rgb(var(--tw-accent-700) / 0.3);
}

/* Disabled state */
.peer:disabled ~ .toggle-track {
	opacity: 0.5;
	cursor: not-allowed;
}

/* ============================================
   TRAVEL MAP STYLING (Lifetime Stats)
   Dark theme overrides for Leaflet
   ============================================ */

#tw-stats-map {
	background-color: rgb(var(--tw-bg-page));
}

/* Override Leaflet zoom control for dark theme */
#tw-stats-map .leaflet-control-zoom a {
	background-color: rgb(var(--tw-bg-elevated)) !important;
	color: rgb(var(--tw-text-primary)) !important;
	border-color: rgb(var(--tw-border-default)) !important;
}

#tw-stats-map .leaflet-control-zoom a:hover {
	background-color: rgb(var(--tw-bg-hover)) !important;
}

/* Light mode overrides for Leaflet */
html.light #tw-stats-map .leaflet-control-zoom a {
	background-color: rgb(var(--tw-bg-surface)) !important;
	color: rgb(var(--tw-text-primary)) !important;
}

/* Custom marker styles */
.tw-map-marker {
	border-radius: 50%;
	border: none;
	box-shadow: 0 0 4px rgba(0, 0, 0, 0.6);
}

.tw-map-marker-airport {
	background-color: rgb(var(--tw-blue-500) / 0.85);
	width: 6px !important;
	height: 6px !important;
	box-shadow: 0 0 3px rgb(var(--tw-blue-500) / 0.6);
}

.tw-map-marker-hotel {
	background-color: rgb(var(--tw-green-400) / 0.8);
	width: 5px !important;
	height: 5px !important;
	box-shadow: 0 0 3px rgb(var(--tw-green-400) / 0.5);
}

/* Airport code labels */
.tw-map-label {
	background: transparent !important;
	border: none !important;
	box-shadow: none !important;
}

.tw-map-label span {
	display: block;
	font-size: 11px;
	font-weight: 600;
	color: rgb(var(--tw-accent-400));
	text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8), 0 0 8px rgba(0, 0, 0, 0.6);
	white-space: nowrap;
	text-align: center;
}

/* Hotel labels in green */
.tw-map-label-hotel span {
	color: rgb(var(--tw-green-400));
	font-size: 10px;
}

/* Tooltip styling */
.tw-map-tooltip {
	background-color: rgb(var(--tw-bg-elevated)) !important;
	border: 1px solid rgb(var(--tw-border-default)) !important;
	border-radius: 8px !important;
	padding: 8px 12px !important;
	color: rgb(var(--tw-text-primary)) !important;
	font-size: 12px !important;
	box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
}

.tw-map-tooltip::before {
	border-top-color: rgb(var(--tw-bg-elevated)) !important;
}

.tw-map-tooltip strong {
	color: rgb(var(--tw-accent-400));
}

.tw-map-tooltip .text-xs {
	font-size: 10px;
	color: rgb(var(--tw-text-muted));
}

/* ============================================
   WIDGET CAROUSEL
   Horizontal scrolling carousel for trip info widgets
   ============================================ */

/* Hide scrollbar but keep scrolling functional */
.scrollbar-hide {
	-ms-overflow-style: none;
	scrollbar-width: none;
	overflow: -moz-scrollbars-none;
}
.scrollbar-hide::-webkit-scrollbar {
	display: none;
	width: 0;
	height: 0;
	background: transparent;
}

/* Clip scroll indicator on widget carousel parent */
/* clip (not hidden): prevents browser scrolling container on button focus */
#widget-carousel-wrapper {
	overflow: hidden;
	overflow: clip;
}

/* Edge fade overlays - gradient to page background */
#widget-carousel-wrapper::before,
#widget-carousel-wrapper::after {
	content: '';
	position: absolute;
	top: 0;
	bottom: 0;
	width: 2.5rem;
	z-index: 10;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.2s;
}

#widget-carousel-wrapper::before {
	left: 0;
	background: linear-gradient(to right, rgb(var(--tw-bg-page)), transparent);
}

#widget-carousel-wrapper::after {
	right: 0;
	background: linear-gradient(to left, rgb(var(--tw-bg-page)), transparent);
}

#widget-carousel-wrapper[data-fade-left]::before {
	opacity: 1;
}

#widget-carousel-wrapper[data-fade-right]::after {
	opacity: 1;
}

/* Equal height widgets via flexbox stretch */
#widget-carousel {
	align-items: stretch;
	touch-action: pan-x;
	overscroll-behavior-y: contain;
}

/* Ensure all direct children are flex containers for equal height */
#widget-carousel > *:not(script):not(style):not(.contents) {
	display: flex;
	flex-direction: column;
}

/* Widget content centering: grows to fill space below heading, centers children */
.widget-content-center {
	flex: 1 1 0%;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 0.375rem;
}

/* ============================================
   Visa & Power destination card (trip widgets carousel).
   "Document card" treatment - mono eyebrow, color-tinted status strip,
   country + plug row, voltage in the foot. Tone driven by data-tone
   ("success" | "warning" | "error"). Empty-state variant uses the same
   chrome with `.widget-visa-card-empty` as the body.
   ============================================ */
.widget-visa-card {
	border-radius: 0.55rem;
	background: rgb(28 28 32);
	border: 1px solid rgb(255 255 255 / 0.07);
	overflow: hidden;
	color: rgb(232 224 210);
	display: flex;
	flex-direction: column;
	box-shadow: none;
}
html.light .widget-visa-card {
	background: rgb(252 247 238);
	border-color: rgb(0 0 0 / 0.08);
	color: rgb(40 35 28);
	box-shadow: 0 1px 0 rgb(255 255 255 / 0.5) inset;
}
.widget-visa-card-eyebrow {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.5rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	padding: 6px 10px 2px;
	opacity: 0.55;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 6px;
}
.widget-visa-card-strip {
	margin: 2px 6px 4px;
	padding: 4px 8px;
	border-radius: 0.35rem;
	display: flex;
	align-items: center;
	gap: 6px;
	font-weight: 700;
	font-size: 0.7rem;
	letter-spacing: 0.01em;
	line-height: 1.1;
}
.widget-visa-card[data-tone="success"] .widget-visa-card-strip { background: rgb(34 175 90 / 0.18); color: rgb(140 220 165); }
.widget-visa-card[data-tone="warning"] .widget-visa-card-strip { background: rgb(220 155 20 / 0.22); color: rgb(245 195 110); }
.widget-visa-card[data-tone="error"]   .widget-visa-card-strip { background: rgb(225 70 70 / 0.20); color: rgb(245 130 130); }
.widget-visa-card[data-tone="info"]    .widget-visa-card-strip { background: rgb(140 160 185 / 0.16); color: rgb(170 190 215); }
html.light .widget-visa-card[data-tone="success"] .widget-visa-card-strip { background: rgb(34 175 90 / 0.14); color: rgb(20 100 50); }
html.light .widget-visa-card[data-tone="warning"] .widget-visa-card-strip { background: rgb(220 155 20 / 0.18); color: rgb(120 75 10); }
html.light .widget-visa-card[data-tone="error"]   .widget-visa-card-strip { background: rgb(225 70 70 / 0.16); color: rgb(150 35 35); }
html.light .widget-visa-card[data-tone="info"]    .widget-visa-card-strip { background: rgb(70 90 115 / 0.12); color: rgb(55 75 100); }
.widget-visa-card-glyph {
	width: 1.15rem;
	height: 1.15rem;
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
.widget-visa-card-glyph svg { width: 100%; height: 100%; display: block; }
.widget-visa-card-duration { margin-left: auto; font-size: 0.65rem; opacity: 0.8; font-weight: 600; }
a.widget-visa-card-link { text-decoration: none; color: inherit; cursor: pointer; transition: filter 0.15s ease, transform 0.15s ease; }
html:not(.touch-device) a.widget-visa-card-link:hover { filter: brightness(1.08); }
a.widget-visa-card-link:active { transform: scale(0.99); }
.widget-visa-card-row {
	padding: 2px 10px 4px;
	display: flex;
	align-items: center;
	gap: 6px;
}
.widget-visa-card-row img {
	width: 1.05rem;
	height: 1.05rem;
	border-radius: 9999px;
	object-fit: cover;
	box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08);
	flex-shrink: 0;
}
.widget-visa-card-name {
	font-size: 0.72rem;
	font-weight: 700;
	line-height: 1;
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.widget-visa-card-plug {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-left: 6px;
	padding: 2px 4px;
	font-size: 0.85rem;
	line-height: 1;
	color: rgb(232 224 210 / 0.55);
	/* Force own GPU composite layer to defeat an Android-WebView paint-
	   duplication bug. Without this, when an adjacent advisory card
	   (sharing .widget-visa-card classes) sits in the same #widget-carousel
	   tile, the WebView compositor paints this element TWICE - once at the
	   correct ~25px in the country row, and once huge (~150-200px) at the
	   bottom-left of the visa card. R5 panel ruled it a paint-duplication
	   issue (PNG swap reproduced the artifact as a PNG). R6 confirmed:
	   translateZ(0) here, will-change:transform here, OR translateZ(0) on
	   the whole .widget-visa-card all break the bug. translateZ is the
	   smallest, most targeted layer-promotion - applies only to ~25px of
	   pixels per card and has no perceptible cost. See diag panel history
	   in trip/_plug_diag_panel.php (since removed). */
	transform: translateZ(0);
}
html.light .widget-visa-card-plug { color: rgb(40 35 28 / 0.55); }
.widget-visa-card-plug-letter {
	font-weight: 400;
	transform: translateY(1px);
	display: inline-block;
}
.widget-visa-card-plug svg { width: 1.55rem; height: 1.55rem; display: block; opacity: 0.5; }
.widget-visa-card-foot {
	margin-top: auto;
	border-top: 1px dashed rgb(255 255 255 / 0.08);
	padding: 4px 10px 5px;
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.55rem;
	letter-spacing: 0.05em;
	opacity: 0.7;
	display: flex;
	align-items: center;
	justify-content: space-between;
}
html.light .widget-visa-card-foot { border-top-color: rgb(0 0 0 / 0.12); }
.widget-visa-card-volt-mismatch { color: rgb(245 130 130); font-weight: 800; }
html.light .widget-visa-card-volt-mismatch { color: rgb(160 35 35); }
.widget-visa-card-empty {
	flex: 1;
	padding: 6px 10px 8px;
	display: flex;
	flex-direction: column;
	gap: 4px;
	justify-content: center;
	overflow-y: hidden;
	border-top: 1px dashed rgb(255 255 255 / 0.08);
}
html.light .widget-visa-card-empty { border-top-color: rgb(0 0 0 / 0.12); }
.widget-visa-card-empty-msg {
	font-size: 0.7rem;
	line-height: 1.2;
	color: rgb(232 224 210 / 0.65);
}
html.light .widget-visa-card-empty-msg { color: rgb(40 35 28 / 0.65); }

/* ============================================
   Common-phrases destination card (trip widgets carousel).
   "Phrasebook stack" treatment - mono eyebrow with flag + language name,
   3 phrase rows (Hello / Bye / Thanks) with English label + phonetic,
   "+N phrases" CTA in the foot. Stressed syllables (CAPS in source) are
   bold + accent-tinted via `tw_renderPhrasePhonetic()`.
   ============================================ */
.widget-phrase-card {
	border-radius: 0.55rem;
	background: rgb(28 28 32);
	border: 1px solid rgb(255 255 255 / 0.07);
	overflow: hidden;
	color: rgb(232 224 210);
	display: flex;
	flex-direction: column;
	box-shadow: none;
}
html.light .widget-phrase-card {
	background: rgb(252 247 238);
	border-color: rgb(0 0 0 / 0.08);
	color: rgb(40 35 28);
	box-shadow: 0 1px 0 rgb(255 255 255 / 0.5) inset;
}
.widget-phrase-card-eyebrow {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.5rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	padding: 6px 10px 9px;
	opacity: 0.6;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 6px;
}
.widget-phrase-card-eyebrow-left {
	display: flex;
	align-items: center;
	gap: 5px;
	min-width: 0;
}
.widget-phrase-card-eyebrow img {
	width: 0.95rem;
	height: 0.95rem;
	border-radius: 9999px;
	object-fit: cover;
	flex-shrink: 0;
}
.widget-phrase-card-eyebrow-lang {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.widget-phrase-card-list {
	padding: 0 10px;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}
.widget-phrase-card-item {
	display: flex;
	align-items: baseline;
	gap: 8px;
	flex: 1;
	border-top: 1px dashed rgb(255 255 255 / 0.06);
	padding: 2px 0;
	line-height: 1.05;
}
html.light .widget-phrase-card-item { border-top-color: rgb(0 0 0 / 0.08); }
.widget-phrase-card-item:first-child { border-top: 0; }
.widget-phrase-card-en {
	font-size: 0.55rem;
	text-transform: uppercase;
	letter-spacing: 0.12em;
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	opacity: 0.55;
	width: 2.7rem;
	flex-shrink: 0;
}
.widget-phrase-card-phon {
	font-size: 0.74rem;
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.widget-phrase-card-cta {
	border-top: 1px dashed rgb(255 255 255 / 0.08);
	padding: 4px 10px 5px;
	font-size: 0.6rem;
	text-align: right;
	color: rgb(245 195 110);
	font-weight: 700;
}
html.light .widget-phrase-card-cta {
	color: rgb(155 100 20);
	border-top-color: rgb(0 0 0 / 0.12);
}

/* Stress-aware phonetic typography (rendered by tw_renderPhrasePhonetic). */
.widget-phrase-syl { font-weight: 500; }
.widget-phrase-syl-stress {
	font-weight: 800;
	color: rgb(245 195 110);
	letter-spacing: 0.005em;
}
html.light .widget-phrase-syl-stress { color: rgb(155 100 20); }
.widget-phrase-syl-sep { opacity: 0.35; padding: 0 1px; font-weight: 400; }

/* ============================================
   Exchange-rate destination card (trip widgets carousel).
   Phrasebook-stack sibling: one card replaces N. Mono eyebrow with home
   currency + count, up to 3 currency rows (flag + ISO code + rate line
   with the number accent-tinted), CTA in the foot showing remaining
   count if any. Tap opens the existing fxCalcModal (twFxInit hydrates).
   ============================================ */
.widget-rate-card {
	border-radius: 0.55rem;
	background: rgb(28 28 32);
	border: 1px solid rgb(255 255 255 / 0.07);
	overflow: hidden;
	color: rgb(232 224 210);
	display: flex;
	flex-direction: column;
	box-shadow: none;
}
html.light .widget-rate-card {
	background: rgb(252 247 238);
	border-color: rgb(0 0 0 / 0.08);
	color: rgb(40 35 28);
	box-shadow: 0 1px 0 rgb(255 255 255 / 0.5) inset;
}
.widget-rate-card-eyebrow {
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	font-size: 0.5rem;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	padding: 6px 10px 9px;
	opacity: 0.6;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 6px;
}
.widget-rate-card-list {
	padding: 0 10px;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
}
.widget-rate-card-item {
	display: flex;
	align-items: baseline;
	gap: 7px;
	flex: 0 0 auto;
	border-top: 1px dashed rgb(255 255 255 / 0.06);
	padding: 3px 0;
	line-height: 1.05;
}
html.light .widget-rate-card-item { border-top-color: rgb(0 0 0 / 0.08); }
.widget-rate-card-item:first-child { border-top: 0; padding-top: 0; }
.widget-rate-card-item img {
	width: 0.85rem;
	height: 0.85rem;
	border-radius: 9999px;
	object-fit: cover;
	flex-shrink: 0;
	align-self: center;
	box-shadow: 0 0 0 1px rgb(0 0 0 / 0.08);
}
.widget-rate-card-code {
	font-size: 0.55rem;
	text-transform: uppercase;
	letter-spacing: 0.12em;
	font-family: ui-monospace, 'SF Mono', Menlo, monospace;
	opacity: 0.55;
	width: 2.4rem;
	flex-shrink: 0;
}
.widget-rate-card-line {
	font-size: 0.74rem;
	flex: 1;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.widget-rate-card-num {
	font-weight: 800;
	color: rgb(245 195 110);
	letter-spacing: -0.005em;
}
html.light .widget-rate-card-num { color: rgb(155 100 20); }
.widget-rate-card-skel {
	display: inline-block;
	height: 0.7rem;
	width: 5rem;
	border-radius: 0.2rem;
	background: rgb(255 255 255 / 0.08);
	animation: pulse 1.6s ease-in-out infinite;
}
html.light .widget-rate-card-skel { background: rgb(0 0 0 / 0.08); }
.widget-rate-card-cta {
	border-top: 1px dashed rgb(255 255 255 / 0.08);
	padding: 4px 10px 5px;
	font-size: 0.6rem;
	text-align: right;
	color: rgb(245 195 110);
	font-weight: 700;
}
html.light .widget-rate-card-cta {
	color: rgb(155 100 20);
	border-top-color: rgb(0 0 0 / 0.12);
}

/* Line clamp for notes preview */
.line-clamp-3 {
	display: -webkit-box;
	-webkit-line-clamp: 3;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

/* ============================================
   SAFE AREA INSETS (iOS home indicator)
   Requires viewport-fit=cover in meta viewport.
   --tw-safe-*  is populated by window.twApplySafeArea() from WTN.getSafeArea()
   in the native app; env() remains the fallback for plain web.
   ============================================ */
.safe-area-bottom {
	padding-bottom: var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px));
}
.pb-safe {
	padding-bottom: var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px));
}

/* ============================================
   GOOGLE PLACES AUTOCOMPLETE DROPDOWN
   Custom dropdown rendered by twSetupPlaceAutocomplete()
   with category-specific icons from place types
   ============================================ */

.tw-pac-dropdown {
	position: absolute;
	left: 0;
	right: 0;
	top: 100%;
	margin-top: 4px;
	background-color: rgb(var(--tw-bg-elevated));
	border: 1px solid rgb(var(--tw-border-default));
	border-radius: 0.5rem;
	box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
	z-index: 9999;
	max-height: 280px;
	overflow-y: auto;
	overscroll-behavior: contain;
}

.tw-pac-item {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	padding: 0.625rem 0.75rem;
	cursor: pointer;
	border-bottom: 1px solid rgb(var(--tw-border-subtle));
}

.tw-pac-item:last-child {
	border-bottom: none;
}

.tw-pac-item:hover,
.tw-pac-item.tw-pac-active {
	background-color: rgb(var(--tw-bg-hover));
}

.tw-pac-icon {
	flex-shrink: 0;
	width: 16px;
	height: 16px;
	color: rgb(var(--tw-text-muted));
}

.tw-pac-icon svg {
	display: block;
}

.tw-pac-text {
	min-width: 0;
	flex: 1;
}

.tw-pac-main {
	display: block;
	color: rgb(var(--tw-text-primary));
	font-size: 0.875rem;
	line-height: 1.25rem;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.tw-pac-main strong {
	color: rgb(var(--tw-accent-400));
	font-weight: 600;
}

.tw-pac-secondary {
	display: block;
	color: rgb(var(--tw-text-secondary));
	font-size: 0.75rem;
	line-height: 1rem;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* Hide scrollbar but keep scrolling (for horizontal filter rows) */
.no-scrollbar {
	-ms-overflow-style: none;
	scrollbar-width: none;
}
.no-scrollbar::-webkit-scrollbar {
	display: none;
}

/* ============================================
   iOS PWA SAFE AREA & VIEWPORT FIXES
   ============================================ */

/* Safe area padding for bottom navigation (iOS notch/home indicator) */
.safe-area-bottom {
  padding-bottom: var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px));
}

/* Fix iOS PWA viewport height issues
   - min-h-screen uses 100vh which doesn't account for iOS dynamic viewport
   - 100dvh (dynamic viewport height) properly handles iOS Safari/PWA
   - Fallback to 100vh for browsers that don't support dvh
*/
@supports (min-height: 100dvh) {
  body.min-h-screen,
  html body[class*="min-h-screen"] {
    min-height: 100dvh;
  }
}

/* PWA standalone mode specific fixes */
@media all and (display-mode: standalone) {
  /* Ensure body fills the viewport properly in PWA mode */
  html, body {
    min-height: 100dvh;
    min-height: -webkit-fill-available;
  }

  /* Add safe area inset to footer padding in PWA mode */
  footer {
    padding-bottom: calc(var(--tw-safe-bottom, env(safe-area-inset-bottom, 0px)) + 1rem);
  }
}

/* ============================================
   LIFETIME STATS MAP - OVERRIDES
   ============================================ */

/* Map tooltip styling (lifetime stats map - theme-aware) */
.tw-map-tooltip {
  background: rgb(var(--tw-bg-elevated) / 0.95) !important;
  border: 1px solid rgb(var(--tw-accent-300) / 0.3) !important;
  border-radius: 6px !important;
  color: rgb(var(--tw-text-primary)) !important;
  font-size: 12px !important;
  padding: 6px 10px !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4) !important;
}

.tw-map-tooltip::before {
  border-top-color: rgb(var(--tw-bg-elevated) / 0.95) !important;
}

/* Leaflet zoom controls - theme-aware, works in both modes */
.leaflet-control-zoom a {
  background: rgb(var(--tw-bg-elevated) / 0.9) !important;
  color: rgb(var(--tw-text-primary)) !important;
  border-color: rgb(var(--tw-border-default) / 0.5) !important;
}

.leaflet-control-zoom a:hover {
  background: rgb(var(--tw-bg-hover) / 0.9) !important;
}

/* ============================================
   AIRCRAFT IMAGE FADE-IN ANIMATION
   ============================================ */

@keyframes aircraft-fade-in {
  from {
    opacity: 0;
    transform: translateX(12px);
  }
  to {
    opacity: 0.9;
    transform: translateX(0);
  }
}

@keyframes aircraft-label-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

/* Reserve minimum height to prevent layout shift */
.aircraft-container {
  min-height: 80px;
}

/* Image starts invisible, animates when loaded */
.aircraft-image {
  opacity: 0;
  filter: drop-shadow(0 0 7px rgba(0, 0, 0, 0.3));
}

.aircraft-image.aircraft-loaded {
  animation: aircraft-fade-in 0.6s ease-out 0.3s both;
}

/* Label starts invisible, animates when sibling image is loaded */
.aircraft-label {
  opacity: 0;
}

.event-section:has(.aircraft-image.aircraft-loaded) > .aircraft-label {
  animation: aircraft-label-fade-in 0.5s ease-out 0.6s forwards;
}

/* Horizontal flip for generic-white aircraft silhouettes (which face the
   opposite direction from the new airline-livery images). Applied wherever
   the rendered image is the no-livery fallback. */
.tw-flip-h {
  transform: scaleX(-1);
}

/* Fade-in keyframe needs to preserve scaleX(-1) - otherwise the
   animation's transform property overrides it and the flip vanishes during
   the 0.6s fade. */
.aircraft-image.tw-flip-h.aircraft-loaded {
  animation: aircraft-fade-in-flipped 0.6s ease-out 0.3s both;
}
@keyframes aircraft-fade-in-flipped {
  from {
    opacity: 0;
    transform: scaleX(-1) translateX(-12px);
  }
  to {
    opacity: 0.9;
    transform: scaleX(-1) translateX(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .aircraft-image.aircraft-loaded,
  .aircraft-image.tw-flip-h.aircraft-loaded,
  .event-section:has(.aircraft-image.aircraft-loaded) > .aircraft-label {
    animation: none;
    opacity: 0.9;
  }
  .event-section:has(.aircraft-image.aircraft-loaded) > .aircraft-label {
    opacity: 1;
  }
  .aircraft-image.tw-flip-h.aircraft-loaded {
    transform: scaleX(-1);
  }
}

/* Live Aircraft Tracking Map */
.tw-aircraft-icon {
  background: none !important;
  border: none !important;
}

#tw-live-track-map {
  background-color: rgb(var(--tw-bg-elevated));
}

/* Satellite imagery - slightly darkened to fit dark theme */
#tw-live-track-map .leaflet-tile-pane {
  filter: brightness(0.7) contrast(1.1);
}
/* Light mode: lift brightness + soften contrast so the satellite
   map reads as a lighter, cleaner canvas alongside cream page chrome. */
html.light #tw-live-track-map .leaflet-tile-pane {
  filter: brightness(1.08) contrast(0.92) saturate(0.95);
}

.tw-airport-tooltip {
  /* Overlaid on satellite imagery (stays dark in both modes) - keep black scrim */
  background: rgba(0, 0, 0, 0.75) !important;
  border: 1px solid rgb(var(--tw-accent-500) / 0.4) !important;
  color: rgb(var(--tw-accent-500)) !important;
  font-size: 11px !important;
  font-weight: 600 !important;
  padding: 2px 6px !important;
  border-radius: 4px !important;
  box-shadow: none !important;
}

.tw-airport-tooltip::before {
  border-top-color: rgba(245, 158, 11, 0.4) !important;
}

/* Pulsating green dot (aircraft position) */
.tw-aircraft-icon {
  background: none !important;
  border: none !important;
}

.tw-pulse-dot {
  position: relative;
  width: 28px;
  height: 28px;
}

.tw-pulse-core {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  background: rgb(var(--tw-success-500));
  border-radius: 50%;
  box-shadow: 0 0 6px 2px rgb(var(--tw-success-500) / 0.5);
}

.tw-pulse-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: 28px;
  margin: -14px 0 0 -14px;
  border-radius: 50%;
  background: rgb(var(--tw-success-500) / 0.25);
  animation: tw-pulse-expand 2s ease-out infinite;
}

@keyframes tw-pulse-expand {
  0% {
    transform: scale(0.5);
    opacity: 0.8;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

/* Stale position: muted dot, no pulse */
.tw-pulse-dot.stale .tw-pulse-core {
  background: rgb(var(--tw-text-muted));
  box-shadow: none;
  opacity: 0.5;
}

.tw-pulse-dot.stale .tw-pulse-ring {
  display: none;
}

/* Route line direction animation (dashes flow origin → destination) */
.tw-route-line {
  animation: tw-dash-flow 2s linear infinite;
}

@keyframes tw-dash-flow {
  to { stroke-dashoffset: -14; }
}

/* Zoom buttons */
.tw-map-zoom {
  position: absolute;
  bottom: 12px;
  right: 12px;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  gap: 1px;
  border-radius: 6px;
  overflow: hidden;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

.tw-map-zoom-btn {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(var(--tw-bg-elevated) / 0.85);
  color: rgb(var(--tw-text-primary));
  font-size: 18px;
  font-weight: 300;
  line-height: 1;
  border: none;
  cursor: pointer;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

html:not(.touch-device) .tw-map-zoom-btn:hover {
  background: rgb(var(--tw-bg-hover) / 0.9);
  color: rgb(var(--tw-text-primary));
}

/* Stale age blink */
.tw-age-stale {
  color: rgb(var(--tw-error-500));
  animation: tw-age-blink 2s ease-in-out infinite;
}
@keyframes tw-age-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

/* Hide Leaflet attribution on live track map */
#tw-live-track-map .leaflet-control-attribution {
  display: none;
}

/* === Timeline connector geometry (per-segment) ===
   Phase 4 (2026-05-13): reverted from the single-continuous-rail design
   back to per-LI segment painting because the user's intent is segmented:
     (a) NO line between disconnected events
     (b) SOLID colored line within a connected pair (flight depart→arrive)
     (c) DOTTED beige line for travel chips (layover / nearby / drive)

   The shared `:is(...)::before` base + per-modifier vars below paint each
   segment independently. Defaults give "through this row + 24px below"
   solid line; modifier classes override individual vars.
     --tl-top     (default 0)         vertical start, relative to LI top
     --tl-bottom  (default -1.5rem)   vertical end, relative to LI bottom
     --tl-fill    (default solid)     repeating-linear-gradient for dashed
     --tl-z       (default auto)      z-index for stacking-context order
     --tl-line    (default border)    line color (set per-event in PHP)
     --tl-alpha   (default 1)         line opacity

   Per-event line color: set by inline style `--tl-line: var(--tw-blue-900)`
   etc. in trip/_day_accordion.php.

   Note on the phase artifact at chip-arrive seam: the OLD design also
   painted a dashed `::after` on arrive cards (extending from icon center
   to LI bottom) when followed by a chip — to "lead into" the chip's dashed
   line. That second dashed source had a different gradient phase from the
   chip's, producing visibly adjacent dashes for specific card heights.
   The :has(+ chip)::after rule is intentionally NOT restored — the chip's
   own dashed line is the SOLE dashed source in the inter-row + chip area,
   so no two dashed gradients can collide. Cost: there is now visible
   empty space between the arrive icon's bottom and the chip (the arrive
   card's lower body has no line). Trade-off accepted as the cleaner of
   the two visuals. */

/* Shared dashed-gradient value, referenced by the dashed modifiers. */
.tl-connect-dashed,
.tl-connect-top-dashed,
.tl-connect-nearby,
.tl-connect-drive {
	--tl-dashed: repeating-linear-gradient(
		to bottom,
		rgb(var(--tl-line, var(--tw-border-default)) / var(--tl-alpha, 1)) 0,
		rgb(var(--tl-line, var(--tw-border-default)) / var(--tl-alpha, 1)) 2px,
		transparent 2px,
		transparent 6px
	);
}

/* Shared ::before geometry. */
:is(
	.tl-connect,
	.tl-connect-end,
	.tl-connect-top,
	.tl-connect-bottom,
	.tl-connect-dashed,
	.tl-connect-top-dashed,
	.tl-connect-nearby
)::before {
	content: '';
	position: absolute;
	inset-inline-start: -1.5rem;
	width: 2px;
	top: var(--tl-top, 0);
	bottom: var(--tl-bottom, -1.5rem);
	background: var(--tl-fill, rgb(var(--tl-line, var(--tw-border-default)) / var(--tl-alpha, 1)));
	z-index: var(--tl-z, auto);
	pointer-events: none;
}

/* Per-modifier vars. Source order matters when an LI carries multiple
   modifier classes (e.g. `tl-connect-end tl-connect-bottom`). At same
   specificity, the LATER rule's vars win — declare -bottom and -top
   first; the more-specific paired modifiers (-end, -top-dashed) come
   AFTER so their --tl-bottom: 0 stops the line at LI bottom even when
   combined with -bottom's --tl-bottom: -5rem. */
.tl-connect-top       { --tl-top: -5rem; --tl-bottom: 0; }                              /* cross-day stub above; SOLID within LI, DASHED in extension via composition rule below */
.tl-connect-bottom    { --tl-bottom: -5rem; }                                           /* cross-day stub below; SOLID within LI, DASHED in extension via composition rule below */
.tl-connect           { --tl-z: 1; }                                            /* solid through row */
.tl-connect-end       { --tl-top: -1.5rem; --tl-bottom: 0; --tl-z: 1; }         /* arrive half of pair: extend above only */
.tl-connect-dashed    { --tl-top: -1rem; --tl-bottom: -1rem; --tl-fill: var(--tl-dashed); }
.tl-connect-top-dashed { --tl-top: -5rem; --tl-bottom: 0;                       /* cross-day dashed stub above only */
                         --tl-fill: var(--tl-dashed); --tl-z: -1;
                         /* The cross-day stub represents the prev-day TRAVEL chip
                            (drive-time / layover) continuing across the day
                            boundary, NOT the flight that happens to be the next
                            day's first event. Override --tl-dashed locally to
                            use border-default (gray) so the stub matches the
                            chip's gray, not the LI's flight blue. */
                         --tl-dashed: repeating-linear-gradient(
                            to bottom,
                            rgb(var(--tw-border-default) / var(--tl-alpha, 1)) 0,
                            rgb(var(--tw-border-default) / var(--tl-alpha, 1)) 2px,
                            transparent 2px,
                            transparent 6px); }
.tl-connect-nearby    { --tl-top: -1rem; --tl-bottom: -1rem; --tl-fill: var(--tl-dashed); }

/* Phase-alignment hook: chip's gradient is shifted by --tl-phase px so the
   dashes pick up where the prev card's dashed ::after left off. JS sets
   --tl-phase per chip in `twAlignChipDashedPhase` (assets/js/app.js)
   based on absolute Y distance to the prev card top. Defaults to 0 when
   not set (e.g. chips that don't follow an arrive-style card). */
.tl-connect-dashed::before,
.tl-connect-nearby::before {
	background-position-y: var(--tl-phase, 0);
}

/* ARRIVE-style cards followed by a chip: paint a dashed ::after from
   icon center down to LI bottom so the line continues through the
   arrive card's lower body and visually leads into the chip's dashed
   line. The two gradients are phase-aligned at runtime via JS so they
   read as ONE continuous dashed line — no "two dots together" artifact. */
:is(.tl-connect-end, .tl-connect-top, .tl-connect-top-dashed):has(+ :is(.tl-connect-dashed, .tl-connect-nearby))::after {
	content: '';
	position: absolute;
	inset-inline-start: -1.5rem;
	width: 2px;
	top: 25px; /* icon center */
	bottom: 0;
	background: repeating-linear-gradient(
		to bottom,
		rgb(var(--tw-border-default)) 0,
		rgb(var(--tw-border-default)) 2px,
		transparent 2px,
		transparent 6px
	);
	pointer-events: none;
}

/* ARRIVE-style cards (-end, -top, -top-dashed) end their solid line AT
   the icon center, not at the LI bottom. Visually the trip "arrives at"
   the icon and stops there — no line continues through the card body or
   into the inter-row gap below. Icon center = LI top + translate-y-1
   (4px) + half icon height (21px) = 25px from LI top. */
.tl-connect-end::before {
	bottom: auto;
	height: calc(1.5rem + 25px);
}
.tl-connect-top::before,
.tl-connect-top-dashed::before {
	bottom: auto;
	height: calc(5rem + 25px);
}

/* Cross-day connected-pair stubs (tl-connect-top on day-2 arrive,
   tl-connect-bottom on day-1 depart) — SOLID colored line, same as
   within-day connected pair. The flight depart→arrive is one connected
   pair regardless of crossing a day boundary; per spec (b) the entire
   pair-line is solid colored. The base `:is(...)::before` rule (line
   3128) plus the per-modifier --tl-top / --tl-bottom vars on
   .tl-connect-top / .tl-connect-bottom (declared above) already paint
   this solid. No additional rules needed here — earlier composition
   attempt overcomplicated; this comment is left as a marker. */

/* tl-connect + tl-connect-top-dashed: dashed ::before above the card
   needs a solid ::after through the row (the event IS paired, so the
   in-row segment matches its paired sibling's solid line). Default
   `bottom: -1.5rem` extends 24px into the inter-row gap below to bridge
   to whatever LI follows (Flight duration pill, paired arrive card,
   etc.) — same as a plain tl-connect would. When followed directly by
   a chip, drop to `bottom: 0` so the chip's own --tl-top: -1rem bridges
   the gap. Specificity: per-class rule (0,0,2,1); :has override (0,0,3,1). */
.tl-connect.tl-connect-top-dashed::after {
	content: '';
	position: absolute;
	inset-inline-start: -1.5rem;
	width: 2px;
	top: 0;
	bottom: -1.5rem;
	background: rgb(var(--tl-line, var(--tw-border-default)) / var(--tl-alpha, 1));
	z-index: 1;
	pointer-events: none;
}

.tl-connect.tl-connect-top-dashed:has(+ :is(.tl-connect-dashed, .tl-connect-nearby))::after {
	bottom: 0;
}

/* tl-connect-drive: added by drive-times.js to the PREV CARD when a drive
   chip hydrates, so the prev card body gets a dashed line through it
   (the prev card otherwise has no connector class — Event/Accommodation).
   Lives on ::after because the card's ::before may already be taken.
   Bounded top:0 → bottom:0 (card body only). The next chip's own dashed
   ::before with top:-1rem extends UP to meet this line at the prev card's
   bottom edge.
   Uses border-default (gray) HARDCODED, NOT the card's `--tl-line`. The
   card's --tl-line is the event color (pink/blue/yellow); using it here
   would paint pink dashes through an Accommodation card body and gray
   dashes through the following chip — the "multicolored dots" artifact. */
.tl-connect-drive { z-index: 0; }
.tl-connect-drive::after {
	content: '';
	position: absolute;
	inset-inline-start: -1.5rem;
	width: 2px;
	top: 0;
	bottom: 0;
	background: repeating-linear-gradient(
		to bottom,
		rgb(var(--tw-border-default)) 0,
		rgb(var(--tw-border-default)) 2px,
		transparent 2px,
		transparent 6px
	);
	z-index: -1;
	pointer-events: none;
}

/* Community Tips - stagger fade-in */
@media (prefers-reduced-motion: no-preference) {
.tip-card {
    animation: tw-tip-fade-in 300ms ease-out both;
  }
.tip-card:nth-child(1) { animation-delay: 0ms; }
.tip-card:nth-child(2) { animation-delay: 50ms; }
.tip-card:nth-child(3) { animation-delay: 100ms; }
.tip-card:nth-child(4) { animation-delay: 150ms; }
.tip-card:nth-child(5) { animation-delay: 200ms; }
.tip-card:nth-child(n+6) { animation-delay: 250ms; }
}
@keyframes tw-tip-fade-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ============================================
   LQIP (Low-Quality Image Placeholder)
   3-color gradient from a packed 32-bit RGBA hex.
   Based on https://github.com/frzi/lqip-css
   ============================================ */

[style*="--lqip:"] {
  /*
   * Bit layout of the packed --lqip color (#RRGGBBAA):
   * |      RR       |      GG       |      BB       |      AA       |
   * |R R R R G G G G|B B B R R R R G|G G G B B B R R|R G G G G B B B|
   * |    color 0 (11-bit)   | color 1 (11-bit)  | color 2 (10-bit)|
   */
  --lqip-c0: color(
    from var(--lqip)
    srgb
    calc(round(down, r * 255 / pow(2,4)) / 15)
    calc(mod(round(down, r * 255), pow(2,4)) / 15)
    calc(round(down, g * 255 / pow(2,5)) / 7)
    / 1
  );

  --lqip-c1: color(
    from var(--lqip)
    srgb
    calc(mod(round(down, g * 255 / 2), pow(2,4)) / 15)
    calc(((mod(round(down, g * 255), 2) * pow(2,3)) + (round(down, b * 255 / pow(2,5)))) / 15)
    calc(mod(round(down, b * 255 / pow(2,2)), pow(2,3)) / 7)
    / 1
  );

  --lqip-c2: color(
    from var(--lqip)
    srgb
    calc((((mod(round(down, b * 255), pow(2,2)) * 2)) + round(down, alpha * 255 / pow(2,7))) / 7)
    calc(mod(round(down, alpha * 255 / pow(2,3)), pow(2,4)) / 15)
    calc(mod(round(down, alpha * 255), pow(2,3)) / 7)
    / 1
  );

  background:
    radial-gradient(
      150% 75% at 80% 100%,
      var(--lqip-c2),
      rgb(from var(--lqip-c2) r g b / 98%) 10%,
      rgb(from var(--lqip-c2) r g b / 92%) 20%,
      rgb(from var(--lqip-c2) r g b / 82%) 30%,
      rgb(from var(--lqip-c2) r g b / 68%) 40%,
      rgb(from var(--lqip-c2) r g b / 32%) 60%,
      rgb(from var(--lqip-c2) r g b / 18%) 70%,
      rgb(from var(--lqip-c2) r g b / 8%) 80%,
      rgb(from var(--lqip-c2) r g b / 2%) 90%,
      transparent
    ),
    radial-gradient(
      100% 75% at 40% 50%,
      var(--lqip-c1),
      rgb(from var(--lqip-c1) r g b / 98%) 10%,
      rgb(from var(--lqip-c1) r g b / 92%) 20%,
      rgb(from var(--lqip-c1) r g b / 82%) 30%,
      rgb(from var(--lqip-c1) r g b / 68%) 40%,
      rgb(from var(--lqip-c1) r g b / 32%) 60%,
      rgb(from var(--lqip-c1) r g b / 18%) 70%,
      rgb(from var(--lqip-c1) r g b / 8%) 80%,
      rgb(from var(--lqip-c1) r g b / 2%) 90%,
      transparent
    ),
    var(--lqip-c0);
}

/* Thumbnail fade-in (trip page cards) */
.tw-thumb {
	opacity: 0;
	transform: scale(1.06);
	transition: opacity 0.45s ease, transform 0.45s ease;
}
.tw-thumb-loaded {
	opacity: 1;
	transform: scale(1);
}
html[data-vt-back] .tw-thumb {
	opacity: 1;
	transform: none;
	transition: none;
}

/* Details/summary accordion */
details[open] > summary .icon-chevron { transform: rotate(180deg); }
.tw-accordion-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.25s ease;
}
.tw-accordion-body > * { overflow: hidden; }
details[open] > .tw-accordion-body { grid-template-rows: 1fr; }
details.tw-closing > .tw-accordion-body { grid-template-rows: 0fr; }

/* Spinner */
@keyframes tw-spin { to { transform: rotate(360deg); } }
.tw-spinner { animation: tw-spin 0.7s linear infinite; }

/* Lifetime-stats country choropleth
   Scope all rules inside .tw-country-map so generic <path> styling
   never leaks onto other SVGs on the page. */
.tw-country-map { line-height: 0; }
.tw-country-map svg {
	display: block;
	width: 100%;
	height: auto;
	overflow: visible;
}
.tw-country-map path,
.tw-country-map circle[data-microstate] {
	fill: rgb(var(--tw-map-unvisited));
	stroke: rgb(var(--tw-map-stroke));
	stroke-width: 0.4;
	stroke-linejoin: round;
	vector-effect: non-scaling-stroke;
	transition: fill 0.18s ease;
}
.tw-country-map path[data-tier="1"],
.tw-country-map circle[data-microstate][data-tier="1"] { fill: rgb(var(--tw-map-tier-1)); }
.tw-country-map path[data-tier="2"],
.tw-country-map circle[data-microstate][data-tier="2"] { fill: rgb(var(--tw-map-tier-2)); }
.tw-country-map path[data-tier="3"],
.tw-country-map circle[data-microstate][data-tier="3"] { fill: rgb(var(--tw-map-tier-3)); }
.tw-country-map path[data-tier="4"],
.tw-country-map circle[data-microstate][data-tier="4"] { fill: rgb(var(--tw-map-tier-4)); }

/* Legend swatches inherit the same tier tokens so they stay in sync with map */
.tw-map-legend-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; }
.tw-map-legend-swatch[data-tier="1"] { background: rgb(var(--tw-map-tier-1)); }
.tw-map-legend-swatch[data-tier="2"] { background: rgb(var(--tw-map-tier-2)); }
.tw-map-legend-swatch[data-tier="3"] { background: rgb(var(--tw-map-tier-3)); }
.tw-map-legend-swatch[data-tier="4"] { background: rgb(var(--tw-map-tier-4)); }

/* ============================================================================
   REFRESH PALETTE — Component-level overrides
   Refined warm-neutral palette + separated event-type vs status color roles.
   Token defs live in :root and html.light at the top of this file. The rules
   below are the component-layer that consumes them.

   Use !important sparingly to win against the compiled Tailwind utility
   specificity (e.g. .bg-card-flight\/90).
   ============================================================================ */

/* Event card backgrounds — replace the heavy filled per-type cards with
   muted type tints + a 3px accent left-border + a hairline soft border on the
   other three sides. Lifts the type signal off the wall of color while keeping
   each card scannable as flight/hotel/transport/activity. */
.bg-card-flight\/90 {
	background-color: var(--tw-flight-bg) !important;
	border-left: 3px solid rgb(var(--tw-flight-accent) / 0.55);
	border-right: 1px solid var(--tw-border-soft);
	border-top: 1px solid var(--tw-border-soft);
	border-bottom: 1px solid var(--tw-border-soft);
}
.bg-card-accommodation\/90 {
	background-color: var(--tw-hotel-bg) !important;
	border-left: 3px solid rgb(var(--tw-hotel-accent) / 0.55);
	border-right: 1px solid var(--tw-border-soft);
	border-top: 1px solid var(--tw-border-soft);
	border-bottom: 1px solid var(--tw-border-soft);
}
.bg-card-transport\/90 {
	background-color: var(--tw-transport-bg) !important;
	border-left: 3px solid rgb(var(--tw-transport-accent) / 0.55);
	border-right: 1px solid var(--tw-border-soft);
	border-top: 1px solid var(--tw-border-soft);
	border-bottom: 1px solid var(--tw-border-soft);
}
.bg-card-generic\/90 {
	background-color: var(--tw-activity-bg) !important;
	border-left: 3px solid rgb(var(--tw-activity-accent) / 0.55);
	border-right: 1px solid var(--tw-border-soft);
	border-top: 1px solid var(--tw-border-soft);
	border-bottom: 1px solid var(--tw-border-soft);
}

/* Timeline icon nodes — OPAQUE background that visually mimics the
   "type accent at 22% on body bg" but masks the trunk line behind the icon.
   Achieved by stacking a tinted gradient over a solid bg-surface layer, so
   the result is fully opaque. Targets the bg-event-*-900 helpers used by
   _day_accordion.php's flight/transport/etc. timeline circles. */
.bg-event-flight-900 {
	background:
		linear-gradient(rgb(var(--tw-flight-accent) / 0.22), rgb(var(--tw-flight-accent) / 0.22)),
		rgb(var(--tw-bg-surface)) !important;
	color: rgb(var(--tw-flight-accent));
}
.bg-event-accommodation-900 {
	background:
		linear-gradient(rgb(var(--tw-hotel-accent) / 0.22), rgb(var(--tw-hotel-accent) / 0.22)),
		rgb(var(--tw-bg-surface)) !important;
	color: rgb(var(--tw-hotel-accent));
}
.bg-event-transport-900 {
	background:
		linear-gradient(rgb(var(--tw-transport-accent) / 0.22), rgb(var(--tw-transport-accent) / 0.22)),
		rgb(var(--tw-bg-surface)) !important;
	color: rgb(var(--tw-transport-accent));
}
.bg-event-generic-900 {
	background:
		linear-gradient(rgb(var(--tw-activity-accent) / 0.22), rgb(var(--tw-activity-accent) / 0.22)),
		rgb(var(--tw-bg-surface)) !important;
	color: rgb(var(--tw-activity-accent));
}

/* Action labels (DEPART / ARRIVE / CHECK-IN / etc.). These read as event-type
   action chips, NOT status pills, so they should pick up the type accent — not
   the status palette. Existing markup uses bg-event-*-900/40 + text-event-*-200
   + border-event-*-500/30. Override via the /40 background, the /30 border, and
   the -200 text class which Tailwind always emits for this widget. */
.bg-event-flight-900\/40 {
	background-color: rgb(var(--tw-flight-accent) / 0.14) !important;
	border-color: rgb(var(--tw-flight-accent) / 0.40) !important;
}
.bg-event-accommodation-900\/40 {
	background-color: rgb(var(--tw-hotel-accent) / 0.14) !important;
	border-color: rgb(var(--tw-hotel-accent) / 0.40) !important;
}
.bg-event-transport-900\/40 {
	background-color: rgb(var(--tw-transport-accent) / 0.14) !important;
	border-color: rgb(var(--tw-transport-accent) / 0.40) !important;
}
.bg-event-generic-900\/40 {
	background-color: rgb(var(--tw-activity-accent) / 0.14) !important;
	border-color: rgb(var(--tw-activity-accent) / 0.40) !important;
}
.text-event-flight-200 { color: rgb(var(--tw-flight-accent)) !important; }
.text-event-accommodation-200 { color: rgb(var(--tw-hotel-accent)) !important; }
.text-event-transport-200 { color: rgb(var(--tw-transport-accent)) !important; }
.text-event-generic-200 { color: rgb(var(--tw-activity-accent)) !important; }

/* Day card header — currently a filled bg-theme-border bar, very heavy.
   Spec asks for headers that blend with the page bg, separated by spacing
   and a soft border, not a fill. Targets the trip-accordion button via its
   data attr so we don't bleed onto unrelated bg-theme-border surfaces.
   Force opaque + drop backdrop-blur/brightness so the trip hero photo
   doesn't bleed through (same fix on body wrapper below). */
[data-trip-accordion-target],
[data-trip-accordion="empty"] > h2 {
	background-color: rgb(var(--tw-bg-page)) !important;
	border-color: var(--tw-border-soft) !important;
	border-width: 1px !important;
	box-shadow: none !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
}
/* Dark-mode seam fix: header sits on bg-page (darker), body sits on bg-surface
   (lighter), so the same rgba border-soft composites darker on the header than
   on the body — the seam between them reads as a discontinuity. Pin the header's
   bottom border to a solid color matching the body's rendered border shade so
   the panel frame reads as one continuous line. Light mode's bg deltas are small
   enough that the seam is imperceptible, so no override there. */
html.dark [data-trip-accordion-target],
html:not(.light) [data-trip-accordion-target] {
	border-bottom-color: rgb(42, 43, 46) !important;
}
/* Day card body wrapper — the .pt-4 div that holds the timeline. Force
   opaque bg-theme-surface even when the trip has a hero photo (default
   markup uses bg-theme-elevated/50 backdrop-blur-[2px] backdrop-brightness-50
   to let the photo show through). Soften the border so the whole accordion
   reads as one continuous surface. */
[data-trip-accordion="collapse"] > div > div {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border-color: var(--tw-border-soft) !important;
	border-width: 1px !important;
	border-top-width: 0 !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
}

/* Top widgets — Quick Notes, Trip Map, Jetlag Forecast, currency, etc.
   The existing markup paints each one with bg-accent-950/30 + accent borders,
   so the row reads as five competing accent cards. Spec wants a single
   homogeneous neutral surface treatment with accent reserved for icons and
   active states. */
.widget-fade-in {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border-color: var(--tw-border-soft) !important;
	box-shadow: var(--tw-shadow-card);
}

/* Trip Map widget — replace the JPG previews with a clean SVG world outline
   per mockup. Strategy: the SVG is rendered as a CSS mask on a pseudo-element
   layer so the landmass color is set explicitly via background-color (not by
   filtering the SVG). This gives clean theme-aware coloring without filter
   side-effects bleeding onto the pin/label. */
#trip-map-widget > img {
	display: none !important;
}
#trip-map-widget .bg-black\/45 {
	background-color: transparent !important;
	z-index: 2;
}
#trip-map-widget {
	background-color: rgb(var(--tw-bg-surface)) !important;
	/* Triple-belt paint containment to keep the ::before world-outline mask
	   inside this card on Android WebView. We've seen overflow:hidden alone
	   fail to clip mask layers (round 8d - 2026-04-28 bled vertically on iOS
	   Safari; 2026-05-01 bled horizontally into the Expenses card on Android).
	   - clip-path: hardware paint clip honored independently of overflow.
	     Radius must match markup's `rounded-lg` (0.5rem); keep in sync.
	   - contain: paint: spec-correct paint containment.
	   - transform: translateZ(0): forces a GPU compositing layer with strict
	     bounds, defeating any compositor glitch during scroll. */
	clip-path: inset(0 round 0.5rem);
	contain: paint;
	transform: translateZ(0);
}
#trip-map-widget::before {
	content: '';
	position: absolute;
	inset: 0;
	background-color: rgb(var(--tw-text-disabled));
	-webkit-mask: url('/assets/im/worldOutline.png') center 30% / 140% auto no-repeat;
	        mask: url('/assets/im/worldOutline.png') center 30% / 140% auto no-repeat;
	pointer-events: none;
	z-index: 0;
}
#trip-map-widget .text-white {
	color: rgb(var(--tw-text-primary)) !important;
	text-shadow: 0 1px 2px rgb(var(--tw-bg-surface) / 0.7) !important;
}
/* Pin marker — sits above the "Trip Map" label, both vertically grouped
   in the upper-middle so the pin tip naturally rests just above the label. */
#trip-map-widget::after {
	content: '';
	position: absolute;
	top: 50%;
	left: 50%;
	width: 14px;
	height: 14px;
	margin: -28px 0 0 -7px;  /* pin tip ~14px above geometric center; label sits below at flex-center */
	background: rgb(var(--tw-brand));
	border: 2px solid rgb(var(--tw-bg-surface));
	border-radius: 50% 50% 50% 0;
	transform: rotate(-45deg);
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
	pointer-events: none;
	z-index: 3;
}
.widget-fade-in.bg-accent-950\/30,
.widget-fade-in.bg-accent-950\/20,
.widget-fade-in.hover\:bg-accent-950\/40:hover,
.widget-fade-in.hover\:bg-accent-950\/50:hover {
	background-color: rgb(var(--tw-bg-surface)) !important;
}
.widget-fade-in.border-accent-500\/50,
.widget-fade-in.border-accent-500\/30,
.widget-fade-in.hover\:border-accent-400:hover {
	border-color: var(--tw-border-soft) !important;
}

/* Bottom action bar — keep the brand-orange primary +Add button dominant and
   quiet the secondary edit/print/share/delete circle buttons so they don't
   compete for attention. The action bar lives in trip/index.php with edit/
   print/share/delete each rendered as bg-theme-surface circles; default styling
   already mostly neutral, just nudge border opacity down. */
#trip-action-bar a,
#trip-action-bar button {
	border-color: var(--tw-border-soft) !important;
}

/* Header dropdown menu items — `text-theme-text-secondary` (#34302B in
   light mode) was reading slightly too dark/grey per user round 7. Bump
   dropdown item text to a slightly lighter, slightly warmer tone. Scoped
   to `ul a` / `ul button` so it doesn't override the appearance-toggle
   buttons (Dark/Light/Auto), which sit OUTSIDE the menu list and need
   their own `text-white` active state. */
#trips-dropdown ul a,
#trips-dropdown ul button,
#user-dropdown ul a,
#user-dropdown ul button {
	color: rgb(58 52 45) !important;
}
html.dark #trips-dropdown ul a,
html.dark #trips-dropdown ul button,
html.dark #user-dropdown ul a,
html.dark #user-dropdown ul button {
	color: rgb(225 218 208) !important;
}

/* ----------------------------------------------------------------------------
   DESIGN V2 — Round 2 typography + light-mode polish
   Addresses user notes 2026-04-28: tighter letter-spacing, day-header sizing,
   white-on-beige surface treatment, timeline ring/icon refinement, pill
   contrast in light mode, double-border fix, tailfin bg removal, jetlag/
   holiday pill toning, event-card line gap tightening.
   ---------------------------------------------------------------------------- */

/* Global letter-spacing nudge — Baloo 2 ships slightly loose by default.
   `tracking-wide` and `tracking-wider` get neutralized so existing markup
   that opted into wider tracking now reads tighter. Body text picks up a
   subtle negative tracking. */
body { letter-spacing: -0.005em; }
.tracking-wide { letter-spacing: 0 !important; }
.tracking-wider { letter-spacing: 0.01em !important; }

/* Day headers — bigger date label, tight tracking. Targets the date span
   inside the trip-accordion button. */
[data-trip-accordion-target] .text-lg {
	font-size: 1.2rem !important;
	letter-spacing: -0.015em !important;
}

/* Day card surface — pin to bg-theme-surface in all states (matches the
   widget-fade-in chrome and the bg-theme-surface class the markup actually
   sets). Round 4 2026-04-28 painted these bg-page (warm beige) with a
   focus-within bump to bg-surface; reverted 2026-05-10 because the beige
   default read as drift from the rest of the trip-page chrome. */
[data-trip-accordion="collapse"] {
	background-color: rgb(var(--tw-bg-surface)) !important;
}
[data-trip-accordion-target] {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border: 1px solid var(--tw-border-soft) !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
}
[data-trip-accordion-target][aria-expanded="true"] {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border-color: var(--tw-border-soft) !important;
}
/* Day header border-bottom — match the day card's perimeter (--tw-border-default).
   Light: 50% alpha so the line stays a soft hairline against the cream surface.
   Dark: full opacity so the line reads against the stone bg without going washed-out.
   Must come AFTER the `border-color` shorthand block above; otherwise that
   shorthand expands to all four `border-{side}-color` longhand properties and
   silently overwrites our `border-bottom-color`. */
[data-trip-accordion-target][aria-expanded="true"] {
	border-bottom-color: rgb(var(--tw-border-default) / 0.5) !important;
}
html.dark [data-trip-accordion-target][aria-expanded="true"] {
	border-bottom-color: rgb(var(--tw-border-default)) !important;
}
/* Dotted bottom border while the accordion is open (or animating closed); the
   solid 1px from the base spec block above takes over once it's fully collapsed.
   Native `border-style: dotted` renders dots too tightly, so we hide the bottom
   border and draw a radial-gradient strip at the bottom edge — dot diameter and
   pitch are both controllable. Pitch = the `background-size` x-value.
   IMPORTANT: must qualify with `html.dark` / `html:not(.light)` to win specificity
   over the dark-mode seam-fix at line 3643 (`html.dark [data-trip-accordion-target]`
   pins border-bottom-color solid). Without that qualifier, dark-mode shows a solid
   line BELOW the dots. */
html [data-trip-accordion-target][aria-expanded="true"],
html [data-trip-accordion-target][data-collapsing="true"] {
	border-bottom-width: 0 !important;
	border-bottom-style: none !important;
	background-image: radial-gradient(circle, rgb(var(--tw-border-default) / 0.6) 1px, transparent 1.5px) !important;
	background-size: 5px 2px !important;
	background-repeat: repeat-x !important;
	background-position: bottom left !important;
}
html.dark [data-trip-accordion-target][aria-expanded="true"],
html.dark [data-trip-accordion-target][data-collapsing="true"],
html:not(.light) [data-trip-accordion-target][aria-expanded="true"],
html:not(.light) [data-trip-accordion-target][data-collapsing="true"] {
	border-bottom-width: 0 !important;
	border-bottom-style: none !important;
	background-image: radial-gradient(circle, rgb(var(--tw-border-default)) 1px, transparent 1.5px) !important;
}

/* Flight hero card — center vertical divider rendered as a dotted strip to
   match the day-accordion header. 2px wide cells, 5px pitch, 1px-radius dots. */
.tw-flight-hero-divider {
	background-image: radial-gradient(circle, rgb(var(--tw-border-default) / 0.6) 1px, transparent 1.5px);
	background-size: 2px 5px;
	background-repeat: repeat-y;
	background-position: top center;
}
html.dark .tw-flight-hero-divider,
html:not(.light) .tw-flight-hero-divider {
	background-image: radial-gradient(circle, rgb(var(--tw-border-default)) 1px, transparent 1.5px);
}

/* Flight Info panel — replaces Tailwind's `divide-y divide-theme-border` with
   a dotted horizontal strip between rows. 1px dots at 3px pitch. The 1px-tall
   transparent border preserves the layout spacing `divide-y` used to add. */
.tw-divide-dotted > * + * {
	border-top: 1px solid transparent;
	background-image: linear-gradient(to right, rgb(var(--tw-border-default) / 0.6) 1px, transparent 1px);
	background-size: 3px 1px;
	background-repeat: repeat-x;
	background-position: top left;
	background-origin: border-box;
}
html.dark .tw-divide-dotted > * + *,
html:not(.light) .tw-divide-dotted > * + * {
	background-image: linear-gradient(to right, rgb(var(--tw-border-default)) 1px, transparent 1px);
}
/* Body — bg-surface in all states to match the header (revert of round 4). */
[data-trip-accordion="collapse"] > div > div {
	background-color: rgb(var(--tw-bg-surface)) !important;
	border-color: var(--tw-border-soft) !important;
	backdrop-filter: none !important;
	-webkit-backdrop-filter: none !important;
}

/* Dark-mode trip-page chrome lift: bump every day-accordion surface one
   step warmer than the original spec so the panels don't read as
   bg-page slabs blending into the page bg. Default = bg-surface (matches
   the Quick Notes / .widget-fade-in chrome at line 2888); current/active
   = bg-elevated (one notch lighter, preserves the "auto-focus" cue).
   Light mode keeps the original spec — its bg-page→bg-surface step is
   already a clear visual lift on cream, and going one notch warmer
   (bg-elevated = beige) would invert the direction. */
html.dark [data-trip-accordion-target],
html.dark [data-trip-accordion-target][aria-expanded="true"],
html:not(.light) [data-trip-accordion-target],
html:not(.light) [data-trip-accordion-target][aria-expanded="true"] {
	background-color: rgb(var(--tw-bg-surface)) !important;
}
html.dark [data-trip-accordion="collapse"] > div > div,
html:not(.light) [data-trip-accordion="collapse"] > div > div {
	background-color: rgb(var(--tw-bg-surface)) !important;
}
html.dark [data-trip-accordion-target][aria-expanded="true"]:focus-within,
html:not(.light) [data-trip-accordion-target][aria-expanded="true"]:focus-within {
	background-color: rgb(var(--tw-bg-elevated)) !important;
}
html.dark [data-trip-accordion="collapse"]:focus-within > div > div,
html:not(.light) [data-trip-accordion="collapse"]:focus-within > div > div {
	background-color: rgb(var(--tw-bg-elevated)) !important;
}
/* Ring-color custom prop follows the same lift so .tw-tl-ring (timeline
   icon halo that masks the trunk overlap) stays invisible against the
   new bg. Mirrors the existing --tw-day-bg block at lines 3117-3123. */
html.dark [data-trip-accordion="collapse"],
html:not(.light) [data-trip-accordion="collapse"] {
	--tw-day-bg: rgb(var(--tw-bg-surface));
}
html.dark [data-trip-accordion="collapse"]:focus-within,
html:not(.light) [data-trip-accordion="collapse"]:focus-within {
	--tw-day-bg: rgb(var(--tw-bg-elevated));
}
/* Connection pills (Drive / Walk / Nearby / normal-layover / cross-day-drive
   / drive-times.js-injected travel-time pills) all paint with
   `bg-theme-elevated/50`. After the day-body lift above, the active day's
   body resolves to `bg-elevated` solid — which makes a `bg-elevated/50` pill
   sitting on it composite back to bg-elevated and disappear. Scope a bump
   to bg-hover (44,40,36) so the pill stays distinct chrome on both the
   default (bg-surface) and active (bg-elevated) body shades, while sitting
   slightly recessed rather than foreground-bright. Scoped to
   `li` descendants so it doesn't catch the day-body wrapper itself (which
   also carries `bg-theme-elevated/50` for the Unsplash-photo variant). */
html.dark [data-trip-accordion] li .bg-theme-elevated\/50,
html:not(.light) [data-trip-accordion] li .bg-theme-elevated\/50 {
	background-color: rgb(var(--tw-bg-hover)) !important;
	/* Knock pill contents two rungs down from theme defaults so the chip
	   reads as recessed chrome rather than foreground bright. Wrapper
	   carries `text-theme-text-secondary`; drop it to `text-disabled` to
	   take the label + icon (icon inherits via currentColor) deep into
	   the muted range. */
	color: rgb(var(--tw-text-disabled)) !important;
}
/* Inner text spans inside the pill use a mix of explicit theme-text
   classes depending on the variant (flight-duration: wrapper carries the
   secondary color, value span uses `text-theme-text`. Layover/connection:
   label span carries `text-theme-text-secondary`, value span uses
   `text-theme-text`, icon also gets `text-theme-text-secondary` from
   tw_layover_pill_classes). Match them all to the wrapper's `text-disabled`
   so label, value, and icon read uniformly muted; value still emphasizes
   via `font-semibold` weight rather than brightness. The attribute
   selector catches `text-theme-text`, `text-theme-text-secondary`, and
   `text-theme-text-muted` in one rule. */
html.dark [data-trip-accordion] li .bg-theme-elevated\/50 [class*="text-theme-text"],
html:not(.light) [data-trip-accordion] li .bg-theme-elevated\/50 [class*="text-theme-text"] {
	color: rgb(var(--tw-text-disabled)) !important;
}

/* Kill the tw-soft-glow mustard halo on day cards. Source: styles.css:269
   `box-shadow: 0 2px 10px rgba(100, 78, 55, 0.10), 0 0 1px rgba(100, 78, 55, 0.08)`
   — that 0 0 1px rgba(100,78,55,...) renders as a faint warm-brown 1px halo
   around every day card, visually reading as a mustard border. Override to
   neutral cool-grey shadow on the day card outer wrapper specifically (don't
   touch tw-soft-glow elsewhere — it's used on dashboard/header/etc.). */
[data-trip-accordion="collapse"].tw-soft-glow,
[data-trip-accordion="empty"].tw-soft-glow {
	box-shadow: 0 2px 8px rgba(20, 20, 25, 0.06) !important;
}

/* Kill the mustard border on today's day-accordion card only.
   `app.js` adds `border-2 border-status-warning-500` to the today's
   day-card as a legacy "today indicator", but we already signal "today"
   via white surface + injected today-bg selector, so the loud amber
   border is redundant *on the day card only*. Other surfaces use this
   class legitimately (warning pills/cards in `dashboard/index.php`,
   `event/_flight_*.php`, `inc/overlap_functions.php` etc) and need the
   real border to read as "warning". Scope the zero-border to the
   day-accordion target so the rest keeps its border. */
[data-trip-accordion-target].border-status-warning-500 {
	border-width: 0 !important;
	border-color: transparent !important;
}

/* Today panel inner-corner alignment. The panel ("today" day card) gets
   `border-2 border-status-warning-500 rounded-xl` from app.js
   (twApplyTodayPanelStyling). Outer radius 12px minus 2px border = inner
   radius 10px. The header button (rounded-base = 8px) and body
   (rounded-b-xl = 12px) inside that panel don't match the 10px inner curve,
   so the button bg and body bg don't reach the panel's inner edge cleanly —
   a faint stepped/compressed gap shows at each corner inside the gold border.
   Align both to 10px so the inner fill follows the panel's inner curve. */
[data-trip-accordion="collapse"].border-status-warning-500 > h2 > [data-trip-accordion-target],
[data-trip-accordion="empty"].border-status-warning-500 > h2 {
	border-top-left-radius: 10px;
	border-top-right-radius: 10px;
	border-bottom-left-radius: 10px;
	border-bottom-right-radius: 10px;
}
[data-trip-accordion="collapse"].border-status-warning-500 > h2 > [data-trip-accordion-target][aria-expanded='true'],
[data-trip-accordion="collapse"].border-status-warning-500 > h2 > [data-trip-accordion-target][data-collapsing='true'] {
	border-bottom-left-radius: 0;
	border-bottom-right-radius: 0;
}
[data-trip-accordion="collapse"].border-status-warning-500 > [id^="collapse-"] > div {
	border-bottom-left-radius: 10px;
	border-bottom-right-radius: 10px;
}

/* Event card left-edge double-border fix. The original markup applies a
   `border` (1px all-sides) + `border-event-{type}-500/15` (color); my prior
   override layered a 3px border-left on top, but Tailwind's per-side longhand
   could leak through. Consolidate via the shorthand `border` reset, then
   re-set the left edge to the type accent. */
.bg-card-flight\/90 {
	background-color: var(--tw-flight-bg) !important;
	border: 1px solid var(--tw-border-soft) !important;
	border-left: 3px solid rgb(var(--tw-flight-accent) / 0.55) !important;
}
.bg-card-accommodation\/90 {
	background-color: var(--tw-hotel-bg) !important;
	border: 1px solid var(--tw-border-soft) !important;
	border-left: 3px solid rgb(var(--tw-hotel-accent) / 0.55) !important;
}
.bg-card-transport\/90 {
	background-color: var(--tw-transport-bg) !important;
	border: 1px solid var(--tw-border-soft) !important;
	border-left: 3px solid rgb(var(--tw-transport-accent) / 0.55) !important;
}
.bg-card-generic\/90 {
	background-color: var(--tw-activity-bg) !important;
	border: 1px solid var(--tw-border-soft) !important;
	border-left: 3px solid rgb(var(--tw-activity-accent) / 0.55) !important;
}

/* Timeline icon ring — must mask the trunk overlap. Math: each LI's trunk
   extends 24px below LI bottom (`bottom: -1.5rem` in tl-connect rules), but
   `mb-4` only puts 16px between LIs, so the previous trunk leaks 8px ABOVE
   the next icon's top. Ring must be ≥8px to fully cover. Use 9px for a 1px
   safety margin against subpixel rounding.

   Ring color follows the day-card body surface via the --tw-day-bg custom
   property — set per-state on the day-accordion wrapper below, so the ring
   stays invisible against whatever bg the day currently shows. */
[data-trip-accordion="collapse"] {
	--tw-day-bg: rgb(var(--tw-bg-surface));
}
[data-trip-accordion="collapse"]:focus-within {
	--tw-day-bg: rgb(var(--tw-bg-surface));
}
.tw-tl-ring {
	--tw-ring-color: var(--tw-day-bg);
	--tw-ring-offset-width: 0px;
	/* Round 8c (user request): bump icon inset from -45px to -50px so the
	   icon's right edge at -8px gives 8px gap to the card (was 3px).
	   Icon: 42×42, inset-inline-start = -50px, center = -29px from LI content.
	   Trunk pseudos overridden below to also sit at -29px so they stay
	   centered through the icon. */
	width: 42px !important;
	height: 42px !important;
	inset-inline-start: -50px !important;
	box-shadow:
		0 -12px 0 -1px var(--tw-day-bg),
		0  12px 0 -1px var(--tw-day-bg) !important;
	/* Frosted-glass treatment: the bg-event-*-900 fill is overridden below
	   per tone to a semi-transparent -500 tint, plus a soft backdrop blur
	   so the day-card surface behind reads through. */
	backdrop-filter: blur(8px);
	-webkit-backdrop-filter: blur(8px);
}
.tw-tl-ring.bg-event-flight-900        { --tw-tl-tone: var(--tw-blue-500);   --tw-tl-tone-wash: var(--tw-blue-900); }
.tw-tl-ring.bg-event-accommodation-900 { --tw-tl-tone: var(--tw-pink-500);   --tw-tl-tone-wash: var(--tw-pink-900); }
.tw-tl-ring.bg-event-transport-900     { --tw-tl-tone: var(--tw-green-500);  --tw-tl-tone-wash: var(--tw-green-900); }
.tw-tl-ring.bg-event-generic-900       { --tw-tl-tone: var(--tw-yellow-500); --tw-tl-tone-wash: var(--tw-yellow-900); }
.tw-tl-ring.bg-event-flight-900,
.tw-tl-ring.bg-event-accommodation-900,
.tw-tl-ring.bg-event-transport-900,
.tw-tl-ring.bg-event-generic-900 {
	background-color: rgb(var(--tw-tl-tone) / 0.25) !important;
	box-shadow:
		0 -12px 0 -1px var(--tw-day-bg),
		0  12px 0 -1px var(--tw-day-bg),
		inset 0 0 0 1px rgb(var(--tw-tl-tone) / 0.5),
		inset 0 1px 0 0 rgb(255 255 255 / 0.18) !important;
}
/* Light mode: the mid-tone -500 body fought the dark-navy -200 glyph
   (dark-on-medium = muddy). Switch the body to the LIGHT pastel -900
   wash, keep the saturated -500 ring + a strong white top sheen — the
   icon now sits on a near-white frost framed by a coloured rim. */
html.light .tw-tl-ring.bg-event-flight-900,
html.light .tw-tl-ring.bg-event-accommodation-900,
html.light .tw-tl-ring.bg-event-transport-900,
html.light .tw-tl-ring.bg-event-generic-900 {
	background-color: rgb(var(--tw-tl-tone-wash) / 0.75) !important;
	box-shadow:
		0 -12px 0 -1px var(--tw-day-bg),
		0  12px 0 -1px var(--tw-day-bg),
		inset 0 0 0 1.5px rgb(var(--tw-tl-tone) / 0.25),
		inset 0 1px 0 0 rgb(255 255 255 / 0.7) !important;
}

/* Trunk pseudo-elements — align ALL trunk variants (solid + dashed + drive
   + nearby + the arrive :has(+chip)::after) to the icon center axis at
   -29px (the base rules use -1.5rem / -24px). Without this, segments
   paint at different x positions and the trunk reads as a zigzag. */
[data-trip-accordion="collapse"] .tl-connect::before,
[data-trip-accordion="collapse"] .tl-connect.tl-connect-top::after,
[data-trip-accordion="collapse"] .tl-connect.tl-connect-top-dashed::after,
[data-trip-accordion="collapse"] .tl-connect-end::before,
[data-trip-accordion="collapse"] .tl-connect-end::after,
[data-trip-accordion="collapse"] .tl-connect-top::before,
[data-trip-accordion="collapse"] .tl-connect-top::after,
[data-trip-accordion="collapse"] .tl-connect-bottom::before,
[data-trip-accordion="collapse"] .tl-connect-dashed::before,
[data-trip-accordion="collapse"] .tl-connect-nearby::before,
[data-trip-accordion="collapse"] .tl-connect-top-dashed::before,
[data-trip-accordion="collapse"] .tl-connect-top-dashed::after,
[data-trip-accordion="collapse"] .tl-connect-drive::after {
	inset-inline-start: -29px !important;
}
/* SVG glyph sized for 42px outer circle. */
.bg-event-flight-900 svg,
.bg-event-accommodation-900 svg,
.bg-event-transport-900 svg,
.bg-event-generic-900 svg {
	width: 26px !important;
	height: 26px !important;
}

/* Round 8 — shift the entire timeline column ~12px left so the day card's
   internal left padding matches the right padding more closely. Drop the OL
   margin-start entirely (was ms-4 mobile / sm:ms-6 desktop) and tighten the
   event-card LI margin-start to 3rem (was ms-12 mobile / sm:ms-14 desktop).
   Tuned via Playwright probe: with OL ms=0 + LI ms=3rem (48px) + icon at
   -start-12 (-48px), icon's left edge sits 12px inside day body's left edge,
   matching the day body's px-3 right padding (12px) so the day card reads
   visually balanced. Layover/duration pill LIs (which use ms-6) untouched —
   they have their own ml-4 inside the pill div. */
[data-trip-accordion="collapse"] > div > div > ol {
	margin-inline-start: 0 !important;
}
[data-trip-accordion="collapse"] > div > div > ol > li:has(> a[href^="/event/"]),
[data-trip-accordion="collapse"] > div > div > ol > li:has(> a[class~="block"][class~="overflow-hidden"]) {
	margin-inline-start: 3rem !important;
}

/* Pill LIs (duration, layover, connection, drive-time) aligned to the same
   left edge as event cards so the timeline reads as a clean column. The
   default `<li class="-mt-2 mb-4 ms-6">` puts pills at 24px + the inner div's
   `ml-4` (16px) = 40px from OL content; cards now at 48px from OL content.
   Bump pill LI ms to 3rem (48px) and zero the inner ml so pill's left edge
   matches card's left edge.

   Selector covers BOTH static pill variants (`<div class="inline-flex
   rounded-full shadow-sm">`) AND the JS-injected drive-time pill which can
   be `<div class="inline-flex rounded-2xl shadow-sm">` OR `<a class="inline-flex
   rounded-full shadow-sm">`. Loosening the rule to "any child of <li> in the
   timeline OL with `inline-flex shadow-sm`" catches all cases. Without this,
   the drive-pill LI stays at ms-6 (24px) and the dashed trunk renders at a
   second x-position — visible as the "doubled dotted line" the user reported. */
[data-trip-accordion="collapse"] ol > li:has(> div[class~="inline-flex"][class~="shadow-sm"]),
[data-trip-accordion="collapse"] ol > li:has(> a[class~="inline-flex"][class~="shadow-sm"]) {
	margin-inline-start: 3rem !important;
}
[data-trip-accordion="collapse"] ol > li > div[class~="inline-flex"][class~="shadow-sm"],
[data-trip-accordion="collapse"] ol > li > a[class~="inline-flex"][class~="shadow-sm"] {
	margin-left: 0 !important;
}

/* Event-card hover: neutralize Tailwind's `hover:brightness-110` (lifts the
   photo on bgfade cards) and replace with a subtle 1px lift + soft drop
   shadow + slightly stronger per-type border tint. Gated to non-touch via
   the html.touch-device convention so iOS doesn't sticky-hover. */
html:not(.touch-device) a[href^="/event/"][class*="border-event-"]:hover {
	filter: none !important;
	transform: translateY(-1px);
	box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.45);
}
html.light:not(.touch-device) a[href^="/event/"][class*="border-event-"]:hover {
	box-shadow: 0 6px 16px -6px rgba(0, 0, 0, 0.14);
}
html:not(.touch-device) a[href^="/event/"][class*="border-event-flight-"]:hover        { border-color: rgb(var(--tw-blue-500)   / 0.45) !important; }
html:not(.touch-device) a[href^="/event/"][class*="border-event-accommodation-"]:hover { border-color: rgb(var(--tw-pink-500)   / 0.45) !important; }
html:not(.touch-device) a[href^="/event/"][class*="border-event-transport-"]:hover     { border-color: rgb(var(--tw-green-500)  / 0.45) !important; }
html:not(.touch-device) a[href^="/event/"][class*="border-event-generic-"]:hover       { border-color: rgb(var(--tw-yellow-500) / 0.45) !important; }

/* Pills inside events (confirmation code kbd, carriage, seat, room labels,
   etc.) — match the airport-code pill style: no border, muted text. The
   airport code pill at _day_accordion.php:278/433 uses
   `text-xs text-theme-text-muted bg-theme-hover rounded px-1.5 py-0.5`;
   we mirror that for all the bg-theme-hover + border-theme-border pills
   inside event cards. */
a[href^="/event/"][class*="border-event-"] .bg-theme-hover.border-theme-border,
a[href^="/event/"][class*="border-event-"] .bg-theme-hover.border.border-theme-border,
a[href^="/event/"][class*="border-event-"] kbd.tw-confirmation-code {
	border: 0 !important;
	color: rgb(var(--tw-text-muted)) !important;
	font-weight: 500 !important;
}

/* Mono font + uppercase for all data pills inside event cards: airport
   code (bg-theme-hover only), confirmation code kbd, and meta chips
   (seat / room / carriage / vehicle / transit). */
a[href^="/event/"][class*="border-event-"] .bg-theme-hover {
	font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
	text-transform: uppercase;
}

/* Bg-fade cards: the standard border-event-*-500/15 alpha gets swamped
   by the photo (which bleeds to the rounded inner edge) so the card
   loses the subtle frame every other event row carries. Bump the alpha
   up to /40, matching per-type hue so the frame still reads as
   color-coded but visible against the photo + gradient. */
.tw-bgfade-card[class*="border-event-flight-"]        { border-color: rgb(var(--tw-blue-500)   / 0.40) !important; }
.tw-bgfade-card[class*="border-event-accommodation-"] { border-color: rgb(var(--tw-pink-500)   / 0.40) !important; }
.tw-bgfade-card[class*="border-event-transport-"]     { border-color: rgb(var(--tw-green-500)  / 0.40) !important; }
.tw-bgfade-card[class*="border-event-generic-"]       { border-color: rgb(var(--tw-yellow-500) / 0.40) !important; }

/* Bg-fade cards (owner-only experiment): partial photo shows behind the
   left zone where the gradient fades, and chips/notes that overlap that
   zone need translucent backgrounds + a backdrop blur so they read as
   floating glass tiles instead of opaque pills stuck on a photo. */
.tw-bgfade-card kbd.tw-confirmation-code,
.tw-bgfade-card span.bg-theme-hover {
	background-color: rgb(var(--tw-bg-hover) / 0.78) !important;
	border-color: rgb(var(--tw-border-default) / 0.5) !important;
	-webkit-backdrop-filter: blur(4px);
	backdrop-filter: blur(4px);
}
.tw-bgfade-card .tw-event-note {
	background-color: rgb(var(--tw-accent-500) / 0.18) !important;
	border-color: rgb(var(--tw-accent-500) / 0.55) !important;
	-webkit-backdrop-filter: blur(4px);
	backdrop-filter: blur(4px);
}

/* Light-mode text-glow override: the dark-mode 4-stop ramp pales out
   against the bright card-color tints in light mode (accommodation =
   rgb(250 238 238) etc), so we bump the inner two stops higher. Same
   sharp-inner -> progressively-softer-outer shape, just at higher
   alphas so titles still lift off the photo. The arbitrary
   text-shadow class carries the type CSS var name in its literal, so
   [class*=...] picks out each variant cleanly. */
html.light .tw-bgfade-card[class*="tw-card-accommodation"] {
	text-shadow:
		0 0 2px  rgb(var(--tw-card-accommodation) / 1),
		0 0 6px  rgb(var(--tw-card-accommodation) / 0.95),
		0 0 14px rgb(var(--tw-card-accommodation) / 0.7),
		0 0 24px rgb(var(--tw-card-accommodation) / 0.35) !important;
}
html.light .tw-bgfade-card[class*="tw-card-transport"] {
	text-shadow:
		0 0 2px  rgb(var(--tw-card-transport) / 1),
		0 0 6px  rgb(var(--tw-card-transport) / 0.95),
		0 0 14px rgb(var(--tw-card-transport) / 0.7),
		0 0 24px rgb(var(--tw-card-transport) / 0.35) !important;
}
html.light .tw-bgfade-card[class*="tw-card-generic"] {
	text-shadow:
		0 0 2px  rgb(var(--tw-card-generic) / 1),
		0 0 6px  rgb(var(--tw-card-generic) / 0.95),
		0 0 14px rgb(var(--tw-card-generic) / 0.7),
		0 0 24px rgb(var(--tw-card-generic) / 0.35) !important;
}

/* Airline tails: replaces the rounded-xl/border/padding tile chrome with
   a drop-shadow only, so the cropped tailfin PNGs sit cleanly against
   the card surface. Dark mode uses a subtle deep-blue outline glow;
   light mode keeps the original white outline + soft cool drop. */
.tw-airline-tail {
	background: transparent !important;
	border: 0 !important;
	padding: 0 !important;
	border-radius: 0 !important;
	filter:
		drop-shadow(0 0 1px rgb(70 110 180 / 0.55))
		drop-shadow(0 0 2px rgb(40 70 140 / 0.45));
}
html.light .tw-airline-tail {
	filter:
		drop-shadow(0 0 1px rgb(255 255 255 / 0.95))
		drop-shadow(0 0 2px rgb(255 255 255 / 0.7))
		drop-shadow(0 1px 2px rgb(15 35 70 / 0.2));
}

/* Trip map SVG — round 8b: lighten the landmass color in light mode (was
   --tw-text-disabled = darker grey) and shrink mask-size from 140% to 95%
   so the full world fits with breathing room (was cropping at 140%).
   Round 8c: bumped 50% lighter — was rgb(190 184 175), now rgb(220 215 210).
   Round 8d (2026-04-28): briefly switched mask-size to `contain` to stop
   bleed onto adjacent carousel card on iOS Safari/Android WebView. But
   `contain` left ~34px empty bands either side of the world (widget is
   208x90, world image is 867x557 / 1.56:1, so height-fit scaled the
   world to 140px wide).
   Round 8e (2026-05-01): back to `cover` for visual fill. Bleed is now
   defeated by paint containment (clip-path + contain:paint +
   transform:translateZ(0)) on #trip-map-widget itself - see comment up
   at the #trip-map-widget rule. Cover crops the top/bottom of the world
   image, which is fine since the main landmasses sit in the middle
   horizontal band. */
#trip-map-widget::before {
	background-color: rgb(220 215 210) !important;
	-webkit-mask: url('/assets/im/worldOutline.png') center / cover no-repeat;
	        mask: url('/assets/im/worldOutline.png') center / cover no-repeat;
}
html.dark #trip-map-widget::before {
	background-color: rgba(255, 255, 255, 0.16) !important;
}

/* Bottom nav (#tw-bottom-nav) — frosted glass + cleaner colors per round 8b.
   The current accent-tinted secondary buttons fight the brand-orange primary;
   neutralize them so the +Add button stays visually dominant. */
#tw-bottom-nav {
	background-color: rgb(var(--tw-bg-surface) / 0.65) !important;
	backdrop-filter: blur(20px) saturate(180%) !important;
	-webkit-backdrop-filter: blur(20px) saturate(180%) !important;
	border-top: 1px solid var(--tw-border-soft);
	box-shadow: 0 -8px 28px rgba(20, 20, 25, 0.10);
}
#tw-bottom-nav .bg-accent-950\/30 {
	background-color: rgb(var(--tw-bg-page)) !important;
}
#tw-bottom-nav .hover\:bg-accent-950\/50:hover {
	background-color: rgb(var(--tw-bg-elevated)) !important;
}
#tw-bottom-nav .border-accent-900\/50 {
	border-color: var(--tw-border-soft) !important;
}
#tw-bottom-nav .text-accent-400 {
	color: rgb(var(--tw-brand)) !important;
}
/* Primary +Add buttons stay full-strength brand orange. */
#bottom-nav-add-event,
#tw-nav-dashboard a[class*="bg-accent-600"] {
	background-color: rgb(var(--tw-brand)) !important;
}
#bottom-nav-add-event:hover,
#tw-nav-dashboard a[class*="bg-accent-600"]:hover {
	background-color: rgb(var(--tw-brand-hover)) !important;
}
/* Delete button stays red but quieter. */
#tw-bottom-nav .bg-status-error-900\/30 {
	background-color: rgb(var(--tw-bg-page)) !important;
}
#tw-bottom-nav .border-status-error-900\/50 {
	border-color: rgb(var(--tw-status-error) / 0.30) !important;
}
#tw-bottom-nav .text-status-error-400 {
	color: rgb(var(--tw-status-error)) !important;
}
/* (SVG glyph size now set in the round-8 block above at 30px — earlier 22px
   rule removed to avoid cascade conflict.) */

/* Type accents — darker variants for the timeline icon glyph + pill text in
   light mode (the bright indigo / amber / etc. read too pale on the new
   light pill backgrounds). Dark mode keeps the original tone. */
html.light {
	--tw-flight-ink: 50 70 175;
	--tw-hotel-ink: 150 70 95;
	--tw-transport-ink: 30 130 105;
	--tw-activity-ink: 175 115 30;
	--tw-food-ink: 165 95 40;
	--tw-note-ink: 110 85 165;
}
html.dark,
html:not(.light) {
	--tw-flight-ink: var(--tw-flight-accent);
	--tw-hotel-ink: var(--tw-hotel-accent);
	--tw-transport-ink: var(--tw-transport-accent);
	--tw-activity-ink: var(--tw-activity-accent);
	--tw-food-ink: var(--tw-food-accent);
	--tw-note-ink: var(--tw-note-accent);
}

/* Repaint timeline icon foreground + action-pill text using the darker -ink
   variants. Override the prior color rules. */
.bg-event-flight-900       { color: rgb(var(--tw-flight-ink)) !important; }
.bg-event-accommodation-900 { color: rgb(var(--tw-hotel-ink)) !important; }
.bg-event-transport-900    { color: rgb(var(--tw-transport-ink)) !important; }
.bg-event-generic-900      { color: rgb(var(--tw-activity-ink)) !important; }

.text-event-flight-200       { color: rgb(var(--tw-flight-ink)) !important; }
.text-event-accommodation-200 { color: rgb(var(--tw-hotel-ink)) !important; }
.text-event-transport-200    { color: rgb(var(--tw-transport-ink)) !important; }
.text-event-generic-200      { color: rgb(var(--tw-activity-ink)) !important; }

/* Action pill backgrounds — slight bump in alpha so they read against the
   card tint, with the now-darker -ink color for text contrast. */
.bg-event-flight-900\/40 {
	background-color: rgb(var(--tw-flight-accent) / 0.18) !important;
	border-color: rgb(var(--tw-flight-accent) / 0.45) !important;
}
.bg-event-accommodation-900\/40 {
	background-color: rgb(var(--tw-hotel-accent) / 0.18) !important;
	border-color: rgb(var(--tw-hotel-accent) / 0.45) !important;
}
.bg-event-transport-900\/40 {
	background-color: rgb(var(--tw-transport-accent) / 0.18) !important;
	border-color: rgb(var(--tw-transport-accent) / 0.45) !important;
}
.bg-event-generic-900\/40 {
	background-color: rgb(var(--tw-activity-accent) / 0.18) !important;
	border-color: rgb(var(--tw-activity-accent) / 0.45) !important;
}

/* Airline tailfin / logo thumb backgrounds — current `tw-logo-thumb` and
   `tw-airline-thumb` paint a beige wash in light mode that fights the
   card tint. Drop the bg + border; let the logo float on the
   card surface. */
.tw-logo-thumb,
.tw-thumb-wrap,
.tw-airline-thumb {
	background-color: transparent !important;
	border-color: transparent !important;
}

/* Jetlag pill + holiday pill — the inline-styled jetlag bg uses
   `--tw-warning-900 / 0.2-0.65`; the holiday pill uses
   `bg-status-warning-500/20 + border /40`. Both read too intense on the new
   lighter surfaces. Tone via lower opacity overrides + softer borders. */
[class*="bg-status-warning-500\/20"],
[class*="bg-status-alert-500\/20"] {
	background-color: rgb(var(--tw-warning-500) / 0.08) !important;
}
[class*="border-status-warning-500\/40"],
[class*="border-status-alert-500\/40"] {
	border-color: rgb(var(--tw-warning-500) / 0.12) !important;
}
/* Jetlag + holiday pill text squash — scoped to the specific bg+text
   combo those pills emit so unrelated `text-status-{caution,error,alert}-200`
   uses (tight-layover value, flight-status banners, admin coverage stats)
   keep their proper hue. Previously this was a broad `[class*="text-status-*-200"]`
   selector that flattened every status-200 use to mustard, making the
   tight-layover pill read as warm beige in light mode. */
[class*="bg-status-warning-500\/20"][class*="text-status-warning-200"],
[class*="bg-status-alert-500\/20"][class*="text-status-alert-200"] {
	color: rgb(var(--tw-warning-500)) !important;
	font-weight: 500;
}
html.light [class*="bg-status-warning-500\/20"][class*="text-status-warning-200"],
html.light [class*="bg-status-alert-500\/20"][class*="text-status-alert-200"] {
	color: rgb(150 115 35) !important;
}

/* drive-times.js multi-mode pill amber tier (between plain and red).
   Uses Tailwind stock amber-* because status-warning/caution palettes
   read as mustard/peach. amber-300 (#fcd34d) is bright amber on dark
   bg, but disappears on light cream. Override to amber-800 in light
   mode for proper contrast and a still-amber hue. */
html.light .tw-severity-warn {
	color: rgb(146 64 14) !important; /* amber-800 */
	background-color: rgb(245 158 11 / 0.15) !important; /* amber-500/15 */
	border-color: rgb(245 158 11 / 0.45) !important;
}

/* Layover / duration / connection pill spacing — reverted per user
   round 7 follow-up: keep the original `-mt-2 mb-4` Tailwind classes. */

/* Event card line gaps — tighten vertical rhythm inside cards. The card
   itself is `p-3`; reduce inner margin-top siblings + tighten line-height
   on the title/meta block. */
a[href^="/event/"][class*="border-event-"] {
	line-height: 1.25;
}
a[href^="/event/"][class*="border-event-"] .mt-2 {
	margin-top: 0.25rem !important;
}
a[href^="/event/"][class*="border-event-"] .mb-2 {
	margin-bottom: 0.25rem !important;
}
a[href^="/event/"][class*="border-event-"] h3,
a[href^="/event/"][class*="border-event-"] .text-base {
	letter-spacing: -0.012em;
}

/* Non-flight cards: drop the time element's `leading-relaxed` (1.625)
   to a tighter line-height so the 20px digits don't sit in a 32.5px
   line box. Flight cards keep `leading-relaxed` because they have 5+
   content rows that absorb the extra leading visually; on
   Accommodation/Transport/Generic cards (3 rows), the extra inside-
   line-box space reads as "too spaced".
   Selector targets `border-event-*` because it's always present;
   `bg-card-*` is blanked out when the bgFade photo experiment is on. */
a[href^="/event/"][class*="border-event-accommodation"] time,
a[href^="/event/"][class*="border-event-transport"] time,
a[href^="/event/"][class*="border-event-generic"] time {
	line-height: 1.25;
}

/* ----------------------------------------------------------------------------
   DESIGN V2 — Round 3 polish per user note 2026-04-28 (continued)
   - Drop the 1px non-accent border so event cards show only the 3px accent
     stripe (the prior shorthand-then-override created a visible step at the
     rounded top-left/bottom-left that read as a "double border")
   - Force timeline trunk to a neutral grey so per-event colors don't bleed
     through the gap area between cards
   - Restore a subtle wash on airline tailfin/logo thumbs
   - Weather pills: tone the non-rain dry pill, add subtle borders
   ---------------------------------------------------------------------------- */

/* Event cards — uniform 1px hairline perimeter using a subtle event-type
   tint (user round 6: "borders look a tad too grey compared to the mockup").
   Per-type tint at 18% alpha gives just enough hue to read as type-coded
   without competing with the accent stripe (already removed). */
.bg-card-flight\/90      { border: 1px solid rgb(var(--tw-flight-accent) / 0.18) !important; }
.bg-card-accommodation\/90 { border: 1px solid rgb(var(--tw-hotel-accent) / 0.18) !important; }
.bg-card-transport\/90   { border: 1px solid rgb(var(--tw-transport-accent) / 0.18) !important; }
.bg-card-generic\/90     { border: 1px solid rgb(var(--tw-activity-accent) / 0.18) !important; }

/* (Timeline trunk overrides reverted per user note round 4 — keeps the
   per-event --tl-line color behavior the existing CSS provides.) */

/* Airline tailfin / logo thumb — white at 50% opacity (round 8b update). */
.tw-logo-thumb,
.tw-thumb-wrap,
.tw-airline-thumb {
	background-color: rgba(255, 255, 255, 0.5) !important;
	border-color: transparent !important;
}

/* Event thumb dark-line fix (user round 7). Two contributors:
   1. The LQIP radial-gradient bg stays painted on the wrapper after the
      image loads, and its outermost stops can show as a dark halo at the
      TOP edge (gradient origin at 80% 100% — opposite corner from top-left).
      Suppress the gradient once the image is loaded via :has().
   2. The wrapper relies on `overflow-hidden` to clip the inner img to its
      `rounded-xl` shape; subpixel AA on the rounded corner can leave a single
      dark pixel at the image edge. Round the img directly so the antialiasing
      is on the image itself, not on the clipping mask. */
.tw-thumb-wrap:has(> img.tw-thumb-loaded),
.tw-thumb-wrap:has(> a > img.tw-thumb-loaded) {
	background: rgba(255, 255, 255, 0.25) !important;
}
.tw-thumb-wrap > img,
.tw-thumb-wrap > a > img {
	border-radius: 0.75rem !important;
}

/* Weather pills — non-rain pill (sun, cloud, etc.) toned down further;
   rain pill kept slightly more present; both get a subtle border. */
.day-weather-dry {
	background-color: transparent !important;
	border: 1px solid var(--tw-border-soft) !important;
	color: rgb(var(--tw-text-muted)) !important;
}
.day-weather-rain {
	background-color: rgb(var(--tw-info-500) / 0.10) !important;
	border: 1px solid rgb(var(--tw-info-500) / 0.25) !important;
	color: rgb(var(--tw-info-500)) !important;
}
html.light .day-weather-rain {
	color: rgb(40 90 130) !important;
}

/* Modals — many modals (e.g. currency calculator) use raw Tailwind classes
   `bg-theme-elevated border-theme-border` directly rather than the
   `.tw-modal-surface` helper. Catch BOTH: target all bg-theme-elevated +
   theme-border elements inside any .tw-modal so the panel surface, header,
   body, footer all read white in light mode (and elevated dark in dark mode).
   Also cool the backdrop scrim. */
.tw-modal-surface,
.tw-modal-body,
.tw-modal-footer,
.tw-modal .bg-theme-elevated,
.tw-modal .bg-theme-surface {
	background-color: rgb(var(--tw-bg-surface)) !important;
}
.tw-modal .border-theme-border {
	border-color: var(--tw-border-soft) !important;
}
html.light .tw-modal {
	background: rgba(30, 30, 35, 0.42) !important;
}
/* Modal header band — slight beige tint vs the white body so the header
   reads as a distinct "title row". Targets the modal-header helper AND the
   common raw-Tailwind pattern of a header div with `border-b border-theme-border`
   used by modals like the currency calculator. Round the top corners to
   match the modal panel's `rounded-lg` (8px) — without this the header band
   has sharp corners that look out of place against the panel's rounded panel. */
.tw-modal-header,
.tw-modal .border-b.border-theme-border {
	background-color: rgb(var(--tw-bg-page)) !important;
	border-top-left-radius: 0.5rem;
	border-top-right-radius: 0.5rem;
}

/* ============================================
   DARK-MODE-ONLY UI TWEAKS (round 9, 2026-04-29)
   - Sharper divider under expanded day-header
   - Modal header sits one step ABOVE the body (lighter, not darker)
   - Tailfin/logo thumbs read as a darker inset, not a beige wash
   - Collapsed + empty accordion days keep full opacity
   ============================================ */
html:not(.light) .tw-modal-header,
html:not(.light) .tw-modal .border-b.border-theme-border {
	background-color: rgb(var(--tw-bg-elevated)) !important;
}
html:not(.light) .tw-logo-thumb,
html:not(.light) .tw-airline-thumb {
	background-color: rgba(0, 0, 0, 0.35) !important;
}
html:not(.light) [data-trip-accordion="collapse"] > h2 > button[aria-expanded="false"],
html:not(.light) [data-trip-accordion="empty"] > h2.opacity-70 {
	opacity: 1 !important;
}

@media print {
	/* Fixed chrome would otherwise overlap content on the printed page. */
	#tw-header,
	#tw-bottom-nav { display: none !important; }

	/* The fixed header reserves pt-16 (4rem) under #swup; drop it on print
	   so there isn't a 4rem blank band at the top of the first page. */
	main#swup { padding-top: 0 !important; }

	/* Force a printable colour scheme regardless of dark/light mode. */
	html, body { background: #fff !important; color: #000 !important; }
}

