/* =============================================================================
   THE JAZZEEXSCAPES TERMINAL — v0.1 STYLES
   =============================================================================
   This file is organized top to bottom in the order things appear on screen:

     1. Global page reset       – basic browser cleanup
     2. Stage & background      – the full-screen photo and its scaling frame
     3. Hotspots                – the invisible glowing areas over each object
     4. Tooltip                 – the floating gold label shown on hover
     5. Modal                   – the placeholder popup shown on click
     6. Intro boarding overlay  – the one-time welcome splash screen
     7. Departures Board close-up – the zoom-in + interactive board panel
     8. Luxury Discovery Lights – the pulsing gold reflections on select objects
     9. Member Access modal    – the Benefit Butler card's dedicated popup
    10. Concierge + Boarding Updates modals – the Concierge Bell's and
                                Branded Pen's dedicated popups
    11. Passport page-turn modal – the Passport's animated opening sequence
    12. Long-message modals    – shared scroll boundary for the Welcome modal
    13. Handwritten Note       – the Coffee Mug's "Meet Jania" note, redesigned

   Search for the ALL CAPS section headers below to jump to a section.
   ============================================================================= */


/* -----------------------------------------------------------------------------
   1. GLOBAL PAGE RESET
   -----------------------------------------------------------------------------
   Small housekeeping rules so the page behaves predictably before we start
   styling anything specific. */

* {
  /* Makes width/height calculations include padding and border, so sizing
     stays predictable everywhere else in this file. */
  box-sizing: border-box;
}

html, body {
  height: 100%;              /* let our full-screen layout below use 100% height */
  margin: 0;                 /* remove the browser's default page margin */
  background: #05040a;       /* near-black, shows as "letterbox" bars on odd screen shapes */
  font-family: 'Georgia', 'Times New Roman', serif; /* elegant serif to match the luxury brand */
  overflow: hidden;          /* prevents scrollbars from appearing */

  /* MOBILE FIX (2026-07-26): plain "overflow: hidden" above is NOT
     enough on real mobile Safari/Chrome to stop the WHOLE PAGE from
     bouncing/scrolling vertically — a live device test showed the Intro,
     the Terminal, and modal content all behaving like one long scrolling
     page instead of three separate fixed-viewport layers. "position:
     fixed" here removes the page itself from the OS/WebView's native
     scroll entirely (the standard fix for this class of iOS Safari bug),
     and "overscroll-behavior: none" is the modern equivalent that also
     stops rubber-band/scroll-chaining on browsers that support it.
     Together they guarantee <body> itself can never move, no matter what
     a visitor's finger does — only ".stage-wrapper" below is ever
     allowed to scroll (and only horizontally), never the page. */
  position: fixed;
  inset: 0;
  width: 100%;
  overscroll-behavior: none;
}


/* -----------------------------------------------------------------------------
   2. STAGE & BACKGROUND
   -----------------------------------------------------------------------------
   ".stage" holds the lobby photo and every hotspot.

   CROP-AWARE HOTSPOT MIGRATION (2026-07-17): this used to lock .stage to
   the background photo's own 3:2 shape (1536 x 1024, matching the old
   lobby-background3.png) so hotspots could stay aligned with simple
   percentages — but that's also what caused black letterbox bars on any
   screen whose own shape didn't match the photo's. .stage now simply
   fills the viewport completely (100vw x 100vh, no locked ratio), and
   ".stage .bg" below uses "object-fit: cover" — the same full-bleed,
   crop-to-fill technique validated in terminal-remastered-preview.html —
   so the background always fills edge-to-edge with no bars, on any
   screen shape. Hotspots are no longer positioned with CSS percentages at
   all: js/script.js now computes each one's on-screen position in real
   pixels from the artwork's own fixed coordinate space, recalculated on
   every resize using the same scale/crop math "cover" itself uses (see
   "COVER-FILL / CROP-AWARE POSITIONING ENGINE" in js/script.js, right
   above its Section 3) — that's what keeps every hotspot correctly
   attached to its object despite this crop. */

.stage-wrapper {
  /* A full-screen flex box holding .stage. Since .stage now always fills
     it completely (100vw x 100vh, see below), there's no letterboxing
     left for this to center — kept only because removing it would be an
     unrelated structural change (see index.html), not because it still
     does any centering work. */
  width: 100vw;
  height: 100vh;
  /* "dvh" (dynamic viewport height) accounts for mobile browser chrome
     (the address bar) showing/hiding as a visitor scrolls — falls back
     silently to the plain "vh" value above on browsers that don't
     support it. Matters most for the pannable mode below: getting the
     available height right is what "an appropriate height for a phone
     screen" (no cropping, no wasted letterbox space) actually depends on. */
  height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.stage {
  position: relative;        /* lets hotspots inside be positioned relative to this box */

  /* Fills the viewport completely — no locked aspect ratio, no
     letterboxing. ".stage .bg" below is what actually crops the
     background photo to match this shape via "object-fit: cover". */
  width: 100vw;
  height: 100vh;
  height: 100dvh;

  overflow: hidden;          /* keeps everything inside neatly clipped to this box */

  /* Powers the Departures Board's "walk up and read it" zoom (see
     style.css Section 7 and js/script.js Section 4). script.js sets
     transform-origin dynamically (in real pixels, converted from the
     board's fixed artwork-space position via the same cover transform
     used for hotspots), then toggles the ".board-zoomed" class below to
     animate the zoom. */
  transition: transform 1s cubic-bezier(0.45, 0, 0.2, 1);
}

.stage.board-zoomed {
  /* This value is duplicated as BOARD_ZOOM_SCALE in js/script.js — if you
     change one, change the other so the zoom amount stays in sync. */
  transform: scale(2.4);
}

.stage .bg {
  /* Fill .stage (now the full viewport) completely, cropping whichever
     axis of the photo overflows rather than stretching or distorting it —
     the standard "background-size: cover" crop-to-fill technique, applied
     here via object-fit since this is a real <img> rather than a CSS
     background-image. This is the change that eliminates the letterbox
     bars the old 3:2-locked .stage used to show. */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center center;

  /* Small polish: stop visitors from accidentally selecting/dragging the
     background image like a normal picture. */
  user-select: none;
  -webkit-user-drag: none;
}


/* -----------------------------------------------------------------------------
   2B. MOBILE PANNABLE MODE
   -----------------------------------------------------------------------------
   On a portrait phone/tablet, "cover" (above) has to crop so much off the
   sides that most of the scene — everything except a narrow center strip —
   becomes impossible to see OR reach, since a hotspot that's been cropped
   out of the viewport can never be hovered or tapped. That's not something
   any background-position tweak can fix; the fundamental problem is that a
   portrait screen is far narrower, relative to its height, than this
   artwork's own wide 1672:941 shape.

   This mode takes the opposite approach: instead of cropping the artwork
   to fill the screen, it shows the ENTIRE artwork at whatever size exactly
   fills the viewport on ONE axis — uncropped, undistorted — and lets it
   overflow on the OTHER axis inside a native, pannable scroll container.
   Real device testing found this same problem also affects mobile
   LANDSCAPE: on a very wide phone screen (e.g. an iPhone in landscape),
   "cover" fills the width exactly but still has to crop the artwork's
   own height, cutting off the lower tabletop objects (Boarding Pass, Pen,
   Benefits Butler Card) with no way to reach them. So js/script.js now
   recognizes THREE display modes (see "MOBILE PANNABLE MODE" in
   js/script.js, right after the cover-transform engine):
     - portrait            (".mode-portrait")            — horizontal-only panning
     - mobile landscape     (".mode-landscape-pannable")  — panning in whichever
                                                             axis(es) actually overflow
     - cover                (neither class)                — desktop/laptop, completely
                                                             unchanged from production
   Both pannable sub-modes share the ".mode-pannable" class for the rules
   below; only "touch-action" (further down) differs between them, since
   portrait is deliberately horizontal-only per the approved brief while
   landscape needs both directions.

   THE KEY INSIGHT that keeps this simple: the SAME cover-transform engine
   that positions hotspots in "cover" mode (computeCoverTransform) needs
   ZERO logic changes to also work here — it already implements the exact
   "cover" math (scale = the LARGER of the two axis ratios), which
   guarantees one axis exactly fills the available space and the other
   meets-or-exceeds it. In "cover" mode that "exceeding" axis gets visually
   cropped; in pannable mode, js/script.js instead sets ".stage" (below)
   to that same exact natural size via inline styles and lets whichever
   axis exceeds the viewport become scrollable — the artwork itself is
   never cropped or distorted in either case, only the TREATMENT of the
   overflow differs. Hotspots, Discovery Lights, and the tooltip all keep
   working completely unchanged, since they're positioned from the same
   coverTransform regardless of which mode is active. */

.mode-pannable .stage-wrapper {
  /* No longer centers a fixed-size stage — instead becomes the native
     pannable viewport. "-webkit-overflow-scrolling: touch" gives real
     momentum/inertia scrolling on iOS Safari; this is a plain native
     scroll container, not a custom drag/swipe implementation, so it
     inherits each platform's own scroll physics and accessibility for
     free (keyboard arrow keys, trackpad, mouse-wheel-shift, etc. all
     already work on a scrollable element with no extra code). Both axes
     are "auto" unconditionally, in BOTH pannable sub-modes — "auto" is a
     no-op on whichever axis doesn't actually overflow (no scrollbar, no
     pan capability, completely inert there), so one shared rule correctly
     handles portrait (only ever overflows horizontally), landscape (may
     overflow horizontally, vertically, or in rare cases both, depending
     on how a given device's own ratio compares to the artwork's), without
     needing separate rules per sub-mode. */
  justify-content: flex-start;
  align-items: flex-start;
  overflow-x: auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;       /* Firefox: hide the scrollbar for a cleaner look */
}

.mode-pannable .stage-wrapper::-webkit-scrollbar {
  display: none;                /* Chrome/Safari: hide the scrollbar */
}

/* MOBILE FIX (2026-07-26, refined 2026-07-30): without this, a real
   finger-swipe could be interpreted by the browser as an attempt to
   scroll/bounce the PAGE instead of panning this element — which is
   exactly what produced the "long scrolling webpage" symptom found in
   live device testing. Portrait stays horizontal-only ("pan-x") per the
   approved brief; landscape allows both directions ("pan-x pan-y") since
   either axis (or both) may need panning there depending on the device's
   own ratio. (Paired with "position: fixed" on <html>/<body> above, which
   removes the page's own ability to scroll at all, as a second layer of
   the same fix.) */
.mode-portrait .stage-wrapper {
  touch-action: pan-x;
}

.mode-landscape-pannable .stage-wrapper {
  touch-action: pan-x pan-y;
}

.mode-pannable .stage {
  /* No longer locked to the viewport size — js/script.js sets this
     element's width/height directly (inline styles) to the artwork's
     exact natural size at the current cover-scale, which is what makes
     it larger than the viewport on whichever axis needs panning. See
     applyPannableStageSize() in js/script.js. ".stage .bg" needs no
     override here at all — it's always sized (by js/script.js) to
     precisely the image's own aspect ratio in this mode, so the base
     ".stage .bg" rule above (object-fit: cover) already renders it with
     zero cropping and zero distortion; a separate rule would just
     duplicate it. */
  position: relative;
  flex-shrink: 0;              /* never let the flex wrapper squeeze this narrower than its content */
  overflow: visible;           /* nothing needs clipping — the whole image is shown, never cropped */
}


/* -----------------------------------------------------------------------------
   2C. MOBILE SWIPE HINT
   -----------------------------------------------------------------------------
   A small, temporary "Swipe to explore the Terminal" pill — shown ONLY the
   first time the Terminal enters pannable mode each browser session (see
   js/script.js), and only ever in pannable mode; never shown in "cover"
   mode (desktop/landscape), so it can never appear where there's nothing
   to swipe. Fades in, holds briefly, then fades out and removes itself —
   js/script.js handles the timing; this just defines the two visual
   states. */

.mobile-swipe-hint {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);
  z-index: 150;                 /* above hotspots/tooltip, below every modal */
  padding: 10px 20px;
  background: rgba(10, 8, 6, 0.85);
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 20px;
  color: #f3e2b3;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 13px;
  letter-spacing: 0.04em;
  white-space: nowrap;
  pointer-events: none;         /* purely informational — never blocks a swipe or tap underneath it */

  opacity: 0;
  transition: opacity 0.6s ease;
}

.mobile-swipe-hint.visible {
  opacity: 1;
}


/* -----------------------------------------------------------------------------
   3. HOTSPOTS
   -----------------------------------------------------------------------------
   Hotspots are invisible <div> elements that script.js creates and places
   on top of the background image (one per object, like the globe, the
   passport, the VIP Lounge door, etc). They're invisible until you hover
   over them, at which point they glow gold. See js/script.js for the list
   of hotspots and exactly where each one is positioned. */

.hotspot {
  position: absolute;        /* positioned using left/top/width/height set by script.js */
  border-radius: 14%;        /* soft, rounded-oval shape instead of a harsh rectangle */
  outline: none;             /* remove the browser's default focus rectangle (we add our own glow instead) */
  background: transparent;   /* invisible until hovered */
  transition: box-shadow 0.35s ease, background 0.35s ease; /* smooth fade-in for the glow */
}

/* "interactive" hotspots open a popup when clicked, so show a pointing-hand cursor. */
.hotspot.interactive {
  cursor: pointer;
}

/* "ambient" hotspots don't do anything on click, so keep the normal cursor. */
.hotspot.ambient {
  cursor: default;
}

/* THE GOLD GLOW EFFECT
   Triggered by mouse hover OR keyboard focus (so keyboard-only visitors get
   the same visual feedback as mouse users). Combines a soft gold radial
   "wash" of color with an outer glow and a subtle inner glow. */
.hotspot:hover,
.hotspot:focus-visible {
  background: radial-gradient(
    ellipse at center,
    rgba(212, 175, 90, 0.20) 0%,   /* warm gold, stronger in the middle */
    rgba(212, 175, 90, 0.08) 55%,  /* fading outward */
    rgba(212, 175, 90, 0) 75%      /* fully transparent at the edges */
  );
  box-shadow:
    0 0 18px 4px rgba(212, 175, 90, 0.55),        /* soft outer glow */
    inset 0 0 24px 2px rgba(255, 221, 150, 0.35); /* soft inner glow */
}

/* Every tabletop object (Globe, Passport, Boarding Pass, Concierge Bell,
   Coffee Mug, Branded Pen, "Benefit Butler Member Access" Card, Rolling
   Luggage, Luxury Travel Bag) has its own permanent "Luxury Discovery
   Light" — a warm reflection of golden-hour light — instead of this
   rectangle glow (see Section 8). script.js tags their hotspot <div>s
   with this extra class, and this rule simply cancels the glow above
   for them. Everything else — including the Luxury Speaker / music
   control, VIP Lounge door, private jet, lounge seating, lounge table,
   and chandelier — keeps the rectangle glow exactly as-is; this rule
   intentionally does NOT touch it. */
.hotspot.discovery-light-object:hover,
.hotspot.discovery-light-object:focus-visible {
  background: none;
  box-shadow: none;
}

/* A couple of ambient, pure-atmosphere objects (Orchid Arrangement, Coffee
   Mug) get NO hover glow at all — not the rectangle, not a Discovery
   Light. They should only ever reveal their tooltip label, never look
   clickable. */
.hotspot.no-hover-glow:hover,
.hotspot.no-hover-glow:focus-visible {
  background: none;
  box-shadow: none;
}

/* DEBUG MODE
   Adding a "debug" class to .stage (e.g. via the browser console:
   `stage.classList.add("debug")`) makes these rules draw a permanent,
   visible outline around every hotspot (even without hovering), which
   makes it easy to check that hotspots are correctly lined up with the
   artwork. There's no on-page button for this anymore (removed for
   launch) — it's a console-only developer tool now, switched off by
   default for real visitors. */
.stage.debug .hotspot {
  background: rgba(212, 175, 90, 0.10);
  box-shadow: inset 0 0 0 1px rgba(212, 175, 90, 0.65); /* gold outline = interactive */
}

.stage.debug .hotspot.ambient {
  box-shadow: inset 0 0 0 1px rgba(150, 190, 212, 0.55); /* blue outline = ambient (easy to tell apart) */
}


/* -----------------------------------------------------------------------------
   4. TOOLTIP (the floating gold label shown on hover)
   -----------------------------------------------------------------------------
   There is only ONE tooltip element in the whole page (see #tooltip in
   index.html). script.js reuses it for every hotspot — filling in the text
   and moving it to a new position — rather than creating a separate label
   for each hotspot. */

.tooltip {
  position: fixed;           /* floats above everything, positioned via left/top set by script.js */
  z-index: 20;                /* stack above the stage/hotspots */
  pointer-events: none;       /* let clicks "pass through" the tooltip to whatever is behind it */
  padding: 6px 14px;
  border-radius: 4px;
  background: rgba(10, 8, 6, 0.85);
  border: 1px solid rgba(212, 175, 90, 0.6);
  color: #f3e2b3;
  font-size: 14px;
  letter-spacing: 0.04em;
  white-space: nowrap;        /* keep the label on one line, never wrap */

  /* Hidden by default (invisible + slightly lower position). The ".visible"
     class below animates it into place. */
  opacity: 0;
  transform: translate(-50%, -12px);
  transition: opacity 0.25s ease, transform 0.25s ease;
}

/* script.js adds this class while a hotspot is hovered/focused. */
.tooltip.visible {
  opacity: 1;
  transform: translate(-50%, -20px); /* floats slightly higher once fully visible */
}


/* -----------------------------------------------------------------------------
   5. MODAL (the placeholder popup shown when an interactive hotspot is clicked)
   -----------------------------------------------------------------------------
   Two parts: ".modal-overlay" is the dark, semi-transparent background that
   covers the whole screen, and ".modal" is the actual popup box centered
   on top of it. */

.modal-overlay {
  position: fixed;
  inset: 0;                   /* shorthand for top/right/bottom/left: 0 (covers full screen) */

  /* Must stay above the Departures Board close-up's backdrop (z-index 90,
     see Section 7) — a row inside that close-up can open this same modal,
     and it needs to end up on TOP of the close-up, not hidden behind it. */
  z-index: 95;

  /* Hidden by default. script.js toggles the ".visible" class to reveal it. */
  display: none;
  align-items: center;         /* center the modal box vertically... */
  justify-content: center;     /* ...and horizontally */

  background: rgba(4, 3, 2, 0.72); /* dims the page behind the popup */
  backdrop-filter: blur(2px);      /* subtle blur for a premium, glassy feel */
}

.modal-overlay.visible {
  display: flex;
}

.modal {
  position: relative;          /* lets the close button be positioned relative to this box */
  width: min(420px, 86vw);     /* comfortable fixed width, but shrinks on small screens */
  padding: 36px 32px 28px;
  background: linear-gradient(160deg, #14110c 0%, #1c1712 100%); /* subtle dark gradient */
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(212, 175, 90, 0.08);
  text-align: center;
  color: #ece2cc;
  animation: modal-in 0.28s ease; /* gentle pop-in animation, defined below */
}

/* Small "pop in" animation: the modal fades in while sliding up slightly
   and growing from 98% to full size. Runs once automatically whenever a
   new .modal element appears (i.e. every time the popup opens). */
@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.modal-title {
  margin: 0 0 14px;
  font-size: 21px;
  letter-spacing: 0.05em;
  color: #d9b968;             /* gold accent color, matches the hover glow */
  font-weight: normal;
}

.modal-body {
  margin: 0 0 18px;
  font-size: 15px;
  line-height: 1.55;
  color: #ddd2b8;
}

/* The small "Prototype v0.1 — not yet connected" reminder text. */
.modal-note {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #7a7062;             /* muted/quiet color since this is a secondary note */
}

.modal-close {
  position: absolute;
  top: 10px;
  right: 14px;
  background: none;
  border: none;
  color: #b8a367;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.modal-close:hover {
  color: #f3e2b3;              /* brightens on hover to show it's clickable */
}


/* -----------------------------------------------------------------------------
   6. INTRO BOARDING OVERLAY (one-time welcome splash screen)
   -----------------------------------------------------------------------------
   This is the full-screen "Welcome to JazzeeXscapes" screen shown once per
   browser session, before a visitor sees the interactive Terminal for the
   first time. It sits on the very top of the page (z-index 100 — higher
   than the modal's 30 and the tooltip's 20), so it visually
   covers the entire Terminal underneath until it's dismissed.

   ".intro-stage" uses the exact same "lock the shape, then shrink to fit
   the screen" technique as the main ".stage" in Section 2 above — just
   with a different aspect ratio (1402:1122, matching the intro artwork
   file's actual pixel dimensions instead of the lobby photo's 3:2). */

.intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #000;

  /* This is what makes the "click Start Exploring" moment feel smooth:
     the whole overlay fades out via opacity instead of just vanishing.
     script.js adds/removes the ".intro-fade-out" class below to trigger it. */
  opacity: 1;
  transition: opacity 0.8s ease;
}

.intro-overlay.intro-fade-out {
  opacity: 0;
  /* Stop the (now invisible) overlay from blocking clicks on the Terminal
     underneath while it's still fading out. */
  pointer-events: none;
}

.intro-stage {
  position: relative;
  aspect-ratio: 1402 / 1122;     /* matches assets/jazzeexscapes-intro.png exactly */
  width: min(100vw, 125vh);      /* 125vh = 100vh * (1402/1122), same trick as .stage */
}

.intro-bg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* THE "START EXPLORING" BUTTON
   This real, clickable button sits exactly on top of the button graphic
   that's already drawn into the background image, matching its black
   background + thin gold border + gold uppercase text styling closely
   enough that the two blend into one seamless-looking button. Using a
   real <button> here (instead of relying on the flat image) is what
   makes it possible to change the label to "PREPARING FOR DEPARTURE..."
   when clicked. */
.intro-start-btn {
  position: absolute;
  left: 31.2%;
  top: 67.6%;
  width: 39%;
  height: 9%;

  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0 6px;

  background: #0d0c0a;
  border: 1.5px solid #cfa64d;
  border-radius: 6px;
  cursor: pointer;

  color: #e8bd5a;
  font-family: 'Helvetica Neue', Arial, sans-serif; /* clean sans-serif, matching the image's button label */
  /* The 9px floor (instead of a higher one) matters on narrow phone
     screens: it's what keeps "START EXPLORING" on a single line instead
     of wrapping and spilling outside the button's border. */
  font-size: clamp(9px, 1.7vw, 20px);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap; /* never wrap to a second line, even on narrow phones */

  transition: background 0.25s ease, transform 0.15s ease;
}

.intro-start-btn:hover,
.intro-start-btn:focus-visible {
  background: #1a1712;
  transform: scale(1.02); /* small, tasteful "lift" instead of anything flashy */
}

.intro-start-btn:disabled {
  cursor: default;
  transform: none;
}

.intro-btn-icon {
  font-size: 0.95em;
}


/* -----------------------------------------------------------------------------
   7. DEPARTURES BOARD CLOSE-UP (zoom-in + interactive board panel)
   -----------------------------------------------------------------------------
   The Departures Board's zoom effect itself (".stage.board-zoomed") is
   defined up in Section 2, right next to the rest of the ".stage" rules,
   since it's really just a variation on the stage's own styling. This
   section covers everything else: the dark backdrop that fades in behind
   the zoom, and the close-up panel with its 8 destination rows. */

.board-backdrop {
  position: fixed;
  inset: 0;
  z-index: 90; /* above hotspots/tooltip (20), below the regular modal (95) and the intro overlay (100) */
  display: flex;
  align-items: center;
  justify-content: center;

  background: rgba(4, 3, 2, 0.68);
  backdrop-filter: blur(3px);

  /* Hidden by default. script.js adds/removes ".visible" to fade it in/out. */
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.5s ease;
}

.board-backdrop.visible {
  opacity: 1;
  pointer-events: auto; /* only clickable once actually visible */
}

.board-closeup {
  position: relative;
  width: min(640px, 92vw);
  max-height: 82vh;
  overflow-y: auto; /* scrolls internally if a very short screen can't fit all 8 rows */

  padding: 30px 26px 22px;
  background: linear-gradient(160deg, #0d0c0a 0%, #17130d 100%);
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 8px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
  color: #ece2cc;

  /* Fades/pops in slightly AFTER the backdrop (script.js delays adding
     this class until the zoom finishes), for that "arrive, then read"
     pacing described in script.js Section 4. */
  opacity: 0;
  transform: translateY(10px) scale(0.98);
  transition: opacity 0.4s ease, transform 0.4s ease;
}

.board-closeup.visible {
  opacity: 1;
  transform: translateY(0) scale(1);
}

.board-closeup-close {
  position: absolute;
  top: 10px;
  right: 14px;
  background: none;
  border: none;
  color: #b8a367;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.board-closeup-close:hover {
  color: #f3e2b3;
}

/* Column layout shared by both the header row and every destination row,
   so their columns always line up with each other. */
.board-closeup-header,
.board-row {
  display: grid;
  grid-template-columns: 56px 1fr 80px 50px;
  gap: 10px;
  align-items: center;
  padding: 10px 8px;
}

.board-closeup-header {
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9c8f6e;
  border-bottom: 1px solid rgba(212, 175, 90, 0.25);
}

.board-row {
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 14px;
  letter-spacing: 0.02em;
  color: #cfc4a6;
  border-bottom: 1px solid rgba(212, 175, 90, 0.12);
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.25s ease;
}

.board-row:hover,
.board-row:focus-visible {
  background: rgba(212, 175, 90, 0.12);
  outline: none;
}

.board-col-destination {
  color: #f3e2b3; /* the destination name is the most important column, so it gets the brightest gold */
}

/* The one inert row (VIP Lounge) — visible for authenticity since it's
   really printed on the board artwork, but deliberately not clickable.
   See the note in js/script.js Section 4 for why. */
.board-row.inert {
  cursor: default;
  opacity: 0.45;
}

.board-row.inert:hover {
  background: none;
}

.board-closeup-hint {
  margin: 16px 0 0;
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: center;
  color: #7a7062;
}


/* -----------------------------------------------------------------------------
   8. LUXURY DISCOVERY LIGHTS (the pulsing gold reflections on select objects)
   -----------------------------------------------------------------------------
   Every tabletop object — Globe, Passport, Boarding Pass, Concierge Bell,
   Coffee Mug, Branded Pen, "Benefit Butler Member Access" Card, Rolling
   Luggage, and the Luxury Travel Bag — gets a small, warm gold Discovery
   Light instead of the rectangle hover-glow from Section 3 (see the
   ".discovery-light-object" override up there).
   js/script.js Section 4 creates one small ".discovery-light" <div> per
   object, positioned at a single point (a corner, an edge, a handle)
   rather than covering the whole hotspot box. The goal is for it to read
   as a natural reflection of golden-hour light — as if the object itself
   is quietly catching the sun — never as a floating UI marker or
   notification.

   Every Discovery Light is built from the same three possible pieces:
     .discovery-light-glow    – the soft pulsing light every one has
     .discovery-light-sparkle – an occasional tiny flash (Concierge Bell, Luggage)
     .discovery-light-sweep   – a slow moving highlight instead of a sparkle (Globe)

   The actual size/speed/brightness numbers come from CSS custom properties
   (--glow-size, --pulse-duration, etc.) that script.js sets per element
   from the "discoveryLight" data on each hotspot — that's what gives each
   object its own "personality" using this one shared set of CSS rules. */

.discovery-light {
  position: absolute;
  transform: translate(-50%, -50%); /* centers the light ON its anchor point */
  width: 0;                          /* the wrapper itself takes up no space — */
  height: 0;                         /* only its (larger, centered) children are visible */
  z-index: 15;                       /* above the image, below the tooltip (20) */
  pointer-events: none;              /* purely decorative — never intercepts hover/clicks */
  transition: opacity 0.5s ease;     /* smooth fade-out when dismissed */
}

/* script.js adds this class right when an object is clicked, then removes
   the Discovery Light from the page entirely once the fade finishes. */
.discovery-light.discovery-light-dismissed {
  opacity: 0;
}

/* THE SOFT PULSING GLOW (every Discovery Light has one) */
.discovery-light-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--glow-size);
  height: var(--glow-size);
  transform: translate(-50%, -50%);
  border-radius: 50%;

  /* Warm metallic gold, brightest at the center and soft at the edges —
     paired with a blur below so it reads as a glow, not a hard-edged dot. */
  background: radial-gradient(
    circle,
    rgba(255, 226, 170, 0.95) 0%,
    rgba(255, 198, 110, 0.6) 38%,
    rgba(212, 175, 90, 0) 72%
  );
  filter: blur(2px);

  opacity: var(--pulse-min-opacity);
  animation: discoveryLightPulse var(--pulse-duration) ease-in-out infinite;
  animation-delay: var(--pulse-delay, 0s);
}

@keyframes discoveryLightPulse {
  0%, 100% {
    opacity: var(--pulse-min-opacity);
    transform: translate(-50%, -50%) scale(0.85);
  }
  50% {
    opacity: var(--pulse-max-opacity);
    transform: translate(-50%, -50%) scale(1.15);
  }
}

/* THE GLINT (nearly every Discovery Light has one)
   The cycle is "glow, glow, glow... GLINT... glow, glow" — a quick flash
   of about 0.2-0.4 seconds out of the full 8-10 second cycle, so it reads
   as an occasional catch of light, never a repeating blink. The peak
   frame stretches the glint sideways into a thin streak instead of a
   round pop — like a sliver of sunlight catching polished metal for an
   instant, not a notification dot flaring up. */
.discovery-light-sparkle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    #fff8e6 0%,
    #ffd98a 45%,
    rgba(255, 217, 138, 0) 75%
  );
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.3, 0.3);
  animation: discoveryLightSparkle var(--sparkle-duration, 9s) ease-in-out infinite;
  animation-delay: var(--sparkle-delay, 2s);
}

@keyframes discoveryLightSparkle {
  0%, 95%, 100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.3, 0.3) rotate(0deg);
  }
  96.5% {
    /* the peak: stretched sideways into a brief streak, not a round dot */
    opacity: 1;
    transform: translate(-50%, -50%) scale(2, 0.55) rotate(-12deg);
  }
  98.5% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.9, 0.9) rotate(-12deg);
  }
}

/* THE GLOBE'S GLINT: A MOVING SWEEP (used instead of a static flash)
   Unlike every other Discovery Light's quick 0.2-0.4s sparkle, this one
   lingers for roughly 1-2 seconds and fades out more slowly than it
   fades in — meant to read as warm light gently sliding across (and
   fading from) a curved, polished surface, never as a flash or a flat
   swept band. Kept intentionally small and thin (a soft short streak,
   not a wide overlay) so it stays a highlight ON the globe rather than
   a graphic effect laid over it. */
.discovery-light-sweep {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 3px;
  height: 34px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 240, 205, 0.85) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  filter: blur(1px);
  opacity: 0;
  transform: translate(-50px, -50%) rotate(20deg);
  animation: discoveryLightSweep var(--sweep-duration, 9.5s) ease-in-out infinite;
  animation-delay: var(--sweep-delay, 3s);
}

@keyframes discoveryLightSweep {
  0%, 89%, 100% {
    opacity: 0;
    transform: translate(-50px, -50%) rotate(20deg);
  }
  92% {
    opacity: 0.8;
    transform: translate(0, -50%) rotate(20deg);
  }
  98% {
    opacity: 0;
    transform: translate(50px, -50%) rotate(20deg);
  }
}

/* Respect visitors who've asked their system to reduce motion: keep a
   soft, steady glow (no pulsing/sparkling/sweeping) instead of turning
   the Discovery Light off entirely. */
@media (prefers-reduced-motion: reduce) {
  .discovery-light-glow,
  .discovery-light-sparkle,
  .discovery-light-sweep {
    animation: none;
  }

  .discovery-light-glow {
    opacity: var(--pulse-max-opacity);
  }

  .discovery-light-sparkle,
  .discovery-light-sweep {
    opacity: 0;
  }
}


/* -----------------------------------------------------------------------------
   9. MEMBER ACCESS MODAL (the Benefit Butler card's dedicated popup)
   -----------------------------------------------------------------------------
   Styled to match the regular modal (Section 5) and the intro overlay's
   button (Section 6) for visual consistency, but built as its own
   dedicated overlay because it needs two real action buttons instead of
   just an × and a line of body text. Same z-index tier as the regular
   modal (95) since the two are mutually exclusive — only one hotspot's
   popup is ever open at a time. */

.member-access-overlay {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(4, 3, 2, 0.72);
  backdrop-filter: blur(2px);
}

.member-access-overlay.visible {
  display: flex;
}

.member-access-modal {
  position: relative;
  width: min(460px, 88vw);
  padding: 40px 34px 30px;
  background: linear-gradient(160deg, #14110c 0%, #1c1712 100%);
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(212, 175, 90, 0.08);
  text-align: center;
  color: #ece2cc;
  animation: modal-in 0.28s ease; /* reuses the @keyframes already defined in Section 5 */
}

.member-access-title {
  margin: 0 0 18px;
  font-size: 22px;
  letter-spacing: 0.05em;
  color: #d9b968;
  font-weight: normal;
}

.member-access-message {
  margin: 0 0 14px;
  font-size: 15px;
  line-height: 1.6;
  color: #ddd2b8;
}

.member-access-message:last-of-type {
  margin-bottom: 28px;
}

.member-access-actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Primary (gold) — "Preview Benefit Butler". Same look as the intro
   overlay's "Start Exploring" button (Section 6), for a consistent
   luxury button style across the whole Terminal. */
.member-access-btn-primary {
  background: #0d0c0a;
  border: 1.5px solid #cfa64d;
  border-radius: 6px;
  padding: 14px 20px;
  cursor: pointer;

  color: #e8bd5a;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;

  transition: background 0.25s ease, transform 0.15s ease;
}

.member-access-btn-primary:hover,
.member-access-btn-primary:focus-visible {
  background: #1a1712;
  transform: scale(1.02);
}

/* Secondary — plain "Close", quieter than the primary action. */
.member-access-btn-secondary {
  background: none;
  border: 1px solid rgba(212, 175, 90, 0.35);
  border-radius: 6px;
  padding: 12px 20px;
  cursor: pointer;

  color: #cbb98a;
  font-family: 'Georgia', 'Times New Roman', serif;
  font-size: 14px;
  letter-spacing: 0.04em;

  transition: background 0.25s ease, border-color 0.25s ease;
}

.member-access-btn-secondary:hover,
.member-access-btn-secondary:focus-visible {
  background: rgba(212, 175, 90, 0.08);
  border-color: rgba(212, 175, 90, 0.6);
}

.member-access-close {
  position: absolute;
  top: 10px;
  right: 14px;
  background: none;
  border: none;
  color: #b8a367;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.member-access-close:hover {
  color: #f3e2b3;
}


/* -----------------------------------------------------------------------------
   10. CONCIERGE + BOARDING UPDATES MODALS (Concierge Bell / Branded Pen)
   -----------------------------------------------------------------------------
   Both dedicated popups reuse every ".member-access-*" class from Section
   10 above (overlay, modal box, title, message, actions, buttons, close
   icon) for a consistent luxury look. The rules below are the small
   extras unique to these two: the Concierge modal's contact-info block,
   and two classes shared by BOTH modals — the quiet footer note and the
   "be the first to hear about" bullet list. */

/* THE CONCIERGE MODAL'S CONTACT-INFO BLOCK
   The two-line "Business Email" / "Business Number" block. Left-aligned
   (unlike the centered message text above it) so the label and value read
   like a clean reference card rather than another sentence. */
.concierge-contact {
  margin: 0 0 24px;
  text-align: left;
}

.concierge-contact-row {
  margin: 0 0 10px;
}

.concierge-contact-row:last-child {
  margin-bottom: 0;
}

.concierge-contact-label {
  display: block;
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9c8f6e; /* same muted gold used for the board's column headers */
  margin-bottom: 2px;
}

.concierge-contact-value {
  font-size: 14px;
  letter-spacing: 0.02em;
  color: #ece2cc;
}

/* SHARED: THE QUIET FOOTER NOTE
   Used by both the Concierge modal ("AI concierge is preparing for
   departure") and the Boarding Updates modal ("every great journey
   begins with staying informed"). Same muted tone as the regular
   modal's ".modal-note", but written as a normal sentence (with its own
   emoji) rather than an uppercase label, so it reads as a soft aside
   instead of a shouted notice. */
.modal-footer-note {
  margin: 20px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: #7a7062;
}

/* SHARED: THE "BE THE FIRST TO HEAR ABOUT" BULLET LIST
   Used by the Boarding Updates modal. Left-aligned like the contact-info
   block above (a scannable list reads better flush-left than centered),
   with muted gold bullet markers to match the rest of the palette
   without competing with the message text's brightness. */
.modal-bullet-list {
  margin: 0 0 18px;
  padding-left: 1.2em;
  text-align: left;
  font-size: 15px;
  line-height: 1.6;
  color: #ddd2b8;
}

.modal-bullet-list li {
  margin-bottom: 4px;
}

.modal-bullet-list li:last-child {
  margin-bottom: 0;
}

.modal-bullet-list li::marker {
  color: #9c8f6e;
}


/* -----------------------------------------------------------------------------
   11. PASSPORT PAGE-TURN MODAL (the Passport's animated opening sequence)
   -----------------------------------------------------------------------------
   Unlike every dedicated modal in Sections 10-11, the Passport doesn't
   just pop up — its cover actually swings open, like a real passport,
   before the "Coming Soon" content is revealed underneath. See
   js/script.js Section 9 for exactly when each piece is triggered; this
   file only defines what each piece looks like and how it animates. */

/* The dark backdrop. Same fade-in treatment as the Departures Board's
   backdrop (Section 7) — a deliberate sequence deserves the same
   unhurried feel, rather than the instant show/hide the simpler
   dedicated modals use. */
.passport-overlay {
  position: fixed;
  inset: 0;
  z-index: 95; /* same tier as every other modal — only one is ever open at a time */
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(4, 3, 2, 0.72);
  backdrop-filter: blur(2px);

  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.passport-overlay.visible {
  opacity: 1;
  pointer-events: auto;
}

/* Sized like an actual passport booklet (portrait, taller than wide),
   with "perspective" set here so the cover's 3D rotation below reads as
   a real turning page instead of a flat squash. */
.passport-stage {
  perspective: 1400px;
  width: min(320px, 82vw);
  aspect-ratio: 5 / 7;
  /* MOBILE FIX (2026-07-30): width + aspect-ratio alone let this grow
     taller than short mobile-landscape viewports (e.g. ~430px tall on an
     iPhone in landscape) with nothing to shrink it back down — the
     passport book itself would render partly off-screen with no way to
     scroll to the cut-off portion. max-height here lets the browser's
     aspect-ratio sizing shrink BOTH width and height together to fit,
     the same way it already does for the width-only constraint above. */
  max-height: 82vh;
}

.passport-book {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
}

/* THE "COMING SOON" CONTENT — sits underneath the cover, filling the
   exact same box. Recreates the same luxury card look as
   ".member-access-modal" (Section 9) rather than reusing that class
   directly, since this one needs to be absolutely positioned (to sit
   exactly behind the cover) instead of centered with a fixed width. */
.passport-content {
  position: absolute;
  inset: 0;
  /* MOBILE FIX (2026-07-30): "overflow-y: auto" used to live here, which
     meant the × close button (a direct child) scrolled away with the
     content on a short screen. Scrolling now happens on the inner
     ".modal-scroll-body" wrapper instead (same fix as every other
     dedicated modal), so the close button — a sibling of that wrapper,
     not inside it — always stays put. */
  padding: 34px 26px 26px;
  background: linear-gradient(160deg, #14110c 0%, #1c1712 100%);
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(212, 175, 90, 0.08);
  color: #ece2cc;
  text-align: center;

  /* Hidden until js/script.js adds "visible" — AFTER the cover finishes
     turning, so the content never appears mid-animation. */
  opacity: 0;
  transition: opacity 0.4s ease;
}

.passport-content.visible {
  opacity: 1;
}

/* THE COVER — the piece that actually animates. Positioned directly on
   top of ".passport-content" (same box, higher z-index), then rotated
   open on its spine like a real passport. */
.passport-cover {
  position: absolute;
  inset: 0;
  z-index: 2;
  transform-style: preserve-3d;
  transform-origin: left center; /* the spine — this is what it "opens" on */
  transform: rotateY(0deg);

  /* Must match PASSPORT_FLIP_DURATION_MS in js/script.js — if you change
     one, change the other. */
  transition: transform 0.9s cubic-bezier(0.65, 0, 0.35, 1);
}

/* js/script.js adds this class a couple of frames after opening the
   modal, which is what actually plays the turn. */
.passport-cover.opening {
  transform: rotateY(-150deg);
}

/* The visible face of the cover — a simple recreation of the desk
   Passport prop's own look (dark leather + gold emblem/lettering), so
   the animation reads as "this same passport, opening" rather than an
   unrelated graphic. "backface-visibility: hidden" keeps it from
   showing a mirrored version of itself once it's rotated past 90°. */
.passport-cover-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;

  background: linear-gradient(160deg, #14110c 0%, #1c1712 100%);
  border: 1px solid rgba(212, 175, 90, 0.55);
  border-radius: 6px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(212, 175, 90, 0.08);
}

.passport-cover-emblem {
  font-size: 40px;
}

.passport-cover-title {
  font-family: 'Georgia', 'Times New Roman', serif;
  font-size: 19px;
  letter-spacing: 0.12em;
  color: #d9b968;
}

.passport-cover-subtitle {
  font-family: 'Helvetica Neue', Arial, sans-serif;
  font-size: 12px;
  letter-spacing: 0.35em;
  color: #cbb98a;
}

/* Respect visitors who've asked their system to reduce motion: skip the
   3D turn and just cross-fade between cover and content instead. */
@media (prefers-reduced-motion: reduce) {
  .passport-cover {
    transition: opacity 0.3s ease;
  }

  .passport-cover.opening {
    transform: none;
    opacity: 0;
    pointer-events: none;
  }
}


/* -----------------------------------------------------------------------------
   12. LONG-MESSAGE MODALS (shared scroll boundary)
   -----------------------------------------------------------------------------
   The Logo's Welcome message has deliberately longer copy than any of
   the ordinary popups in Sections 9-11, so it adds this class on top
   of ".member-access-modal" for its own scroll boundary — otherwise its
   content risks overflowing off the bottom of a short screen. (Meet
   Jania used to reuse this too, before Section 13 gave it its own
   handwritten-note presentation with its own scroll boundary.) */
.modal-scrollable {
  max-height: 86vh;
  overflow-y: auto;
}

/* MOBILE FIX (2026-07-30): ".modal-scrollable" above (and ".board-closeup"
   in Section 7) make the OUTER modal box itself scroll — which works for
   reading long content, but the × close button is a child of that same
   box, so scrolling down carries the close button away with it, leaving
   it unreachable without scrolling back to the top first. A live device
   test confirmed this on the Welcome modal specifically (previously
   assumed fine).
   ".modal-scroll-body" is the fix, applied to every dedicated modal that
   was reported cut off in mobile landscape (Passport, VIP Lounge,
   Layover Lounge, Private Jet, Information Desk, Benefits Butler,
   Concierge Bell, Departures Board's message-style rows, and the rest
   sharing ".member-access-modal") — instead of the OUTER modal box
   scrolling, only an INNER wrapper around the title/message/actions
   content scrolls, while the × button stays a sibling of that wrapper,
   never inside it, so it can never scroll out of reach. Visually
   identical to before when content already fits (the wrapper's max-height
   is never reached, so it never shows a scrollbar or clips anything) —
   only engages on genuinely tall content. */
.modal-scroll-body {
  max-height: 70vh;
  overflow-y: auto;
}


/* -----------------------------------------------------------------------------
   13. HANDWRITTEN NOTE (the Coffee Mug's "Meet Jania" redesign)
   -----------------------------------------------------------------------------
   A signature Terminal moment: instead of the usual dark
   ".member-access-modal" popup, the Coffee Mug reveals a warm, personal
   note that's meant to feel like something Jania actually left behind —
   cream stationery, a whisper of paper grain, a slight natural tilt,
   soft fold creases (as if once folded in thirds), and a gentle
   "unfold and fade in" entrance rather than the usual instant pop-in.
   The outer dark backdrop still comes from ".member-access-overlay"
   (Section 9) — only what's INSIDE it is entirely custom.

   Structure: ".handwritten-note-wrap" is a centered flex column holding
   two independent pieces — the paper (".handwritten-note") and the
   action buttons (".handwritten-note-actions", reusing the ordinary
   ".member-access-btn-*" look from Section 9) — so the buttons visibly
   sit OUTSIDE the paper, like Terminal UI chrome underneath a found
   object, rather than being printed on the note itself. */

.handwritten-note-wrap {
  display: flex;
  flex-direction: column;
  align-items: stretch; /* lets the paper AND the button row both fill this same width */
  gap: 18px;
  width: min(460px, 90vw);
  max-height: 92vh;
}

.handwritten-note {
  position: relative;
  max-height: 62vh;
  overflow-y: auto;
  padding: 46px 40px 34px;

  /* Warm cream stationery, gently mottled rather than a flat fill. */
  background:
    radial-gradient(ellipse at 18% 12%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 40%),
    radial-gradient(ellipse at 85% 90%, rgba(120, 95, 60, 0.10) 0%, rgba(120, 95, 60, 0) 45%),
    linear-gradient(160deg, #f7f0e2 0%, #ece0c8 100%);

  /* A soft, slightly irregular corner radius — a "cut by hand, not a
     machine" imperfection, rather than four identical rounded corners. */
  border-radius: 3px 14px 5px 11px / 10px 4px 16px 5px;

  /* A believable drop shadow (paper resting on the lounge table below
     it) plus the faintest warm edge, instead of the modals' gold border. */
  box-shadow:
    0 26px 50px rgba(0, 0, 0, 0.45),
    0 3px 8px rgba(0, 0, 0, 0.25),
    0 0 0 1px rgba(120, 95, 60, 0.12);

  color: #4a3d2c; /* warm ink-brown, not flat black */

  /* The natural, ever-so-slightly-crooked tilt of a handwritten note
     rather than a perfectly square popup. */
  transform: rotate(-0.6deg);
  transform-origin: top center;

  animation: handwrittenNoteUnfold 0.7s cubic-bezier(0.22, 1, 0.36, 1);
}

/* A whisper of paper grain — a tiny inline SVG noise filter, so this
   never depends on an external texture image being reachable. Kept very
   faint on purpose ("slight paper texture," not a scanned photo). */
.handwritten-note::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
}

/* Two faint horizontal creases, as if this letter once traveled folded
   in thirds — a small, easy-to-miss detail that reads as "real object"
   rather than "graphic design." */
.handwritten-note::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background-image:
    linear-gradient(180deg, transparent 32%, rgba(74, 61, 44, 0.07) 33%, rgba(74, 61, 44, 0.03) 34%, transparent 35.5%),
    linear-gradient(180deg, transparent 65%, rgba(74, 61, 44, 0.07) 66%, rgba(74, 61, 44, 0.03) 67%, transparent 68.5%);
}

/* The unfold-and-fade entrance: starts collapsed toward the top edge
   (as if still folded) and gently drops open, rather than the usual
   modals' instant pop-in. Subtle, per the brief — one motion, ~0.7s. */
@keyframes handwrittenNoteUnfold {
  0% {
    opacity: 0;
    transform: rotate(-0.6deg) scaleY(0.22) translateY(-14px);
  }
  55% {
    opacity: 1;
  }
  100% {
    opacity: 1;
    transform: rotate(-0.6deg) scaleY(1) translateY(0);
  }
}

@media (prefers-reduced-motion: reduce) {
  .handwritten-note {
    animation: none;
  }
}

/* An ink-brown × instead of the modals' gold one — gold read oddly
   against warm cream, brown sits naturally on the page. */
.handwritten-note-close {
  position: absolute;
  top: 8px;
  right: 14px;
  background: none;
  border: none;
  color: rgba(74, 61, 44, 0.55);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.handwritten-note-close:hover {
  color: rgba(74, 61, 44, 0.85);
}

/* THE HANDWRITING ITSELF
   "Caveat" for anything meant to be read (the heading and the letter
   body) — cursive in spirit but still legible at note size. "Dancing
   Script" is reserved for Jania's actual signature below, so the one
   moment that's supposed to look "just signed" reads differently from
   the rest of the handwriting. */
.handwritten-note-heading {
  margin: 0 0 14px;
  font-family: 'Caveat', 'Segoe Script', cursive;
  font-weight: 700;
  font-size: 32px;
  line-height: 1.2;
  color: #4a3d2c;
}

.handwritten-note-text {
  margin: 0 0 14px;
  font-family: 'Caveat', 'Segoe Script', cursive;
  font-weight: 500;
  font-size: 21px;
  line-height: 1.5;
  color: #4a3d2c;
}

.handwritten-note-text:last-of-type {
  margin-bottom: 22px;
}

.handwritten-signoff {
  margin: 0;
  font-family: 'Caveat', 'Segoe Script', cursive;
  font-weight: 500;
  font-size: 21px;
  color: #4a3d2c;
}

/* The signature itself — larger, bolder, a different (more flowing)
   script, as if it were the one part of the note actually signed by
   hand rather than written out. */
.handwritten-signature {
  margin: 2px 0 0;
  font-family: 'Dancing Script', 'Segoe Script', cursive;
  font-weight: 700;
  font-size: 36px;
  color: #3a2f21;
}

.handwritten-signature-heart {
  font-size: 0.62em;
  vertical-align: middle;
  margin-left: 2px;
}

/* THE ACTION ROW — deliberately OUTSIDE the paper, styled like ordinary
   Terminal UI (reusing Section 9's button classes as-is) rather than
   like part of the note, for the "found object vs. app" contrast
   described in the brief. */
.handwritten-note-actions {
  width: 100%;
}

