/* ───────────────────────────────────────────
   Z-INDEX SCALE — single source of truth
   (header.css loads on every page, so :root tokens
   defined here are available site-wide.)

   Three bands. Higher band always wins.

   ── PAGE (1–99): things layered on page content
        --z-mobile-cta      mobile sticky "Plan your visit" bar
   ── HEADER (100–199): global nav — above page, BELOW overlays
        --z-header          header bar / mega-menu
        --z-header-actions  logo / right-side action buttons
        --z-header-overlay  header search-expand / profile dropdown
   ── OVERLAY (1000+): things the user OPENS — cover the header
        --z-overlay-backdrop  dim/catcher behind sheets & modals
        --z-overlay-sheet     bottom sheets / guide / procedure sheets
        --z-overlay-modal     inquiry / report / claim modals
        --z-tooltip           popovers/tooltips (above sheets+modals)
        --z-lightbox          fullscreen photo gallery (topmost)

   Component-internal stacking (close buttons, sheet nav arrows,
   sticky headers INSIDE a sheet) stays as small local values
   (1, 2, 5, 10) — they only compete within their own parent and
   don't need a global token.
   ─────────────────────────────────────────── */
/* 공유 읽기 타이포 `.prose` — 별도 파일(사이트=여기 @import, admin=직접 link). SSOT. */
@import url('/partials/prose.css?v=1');
:root {
    --z-mobile-cta:       60;
    --z-header:           100;
    --z-header-actions:   110;
    --z-header-overlay:   120;
    --z-overlay-backdrop: 1000;
    --z-overlay-sheet:    1100;
    --z-overlay-modal:    1200;
    --z-tooltip:          1300;
    --z-lightbox:         1400;

    /* Modal / overlay animation — single source of truth (see CLAUDE.md
       "Modal animation"). Used by the shared .ktg-modal scaffold below and
       referenced by every centred modal so timing never drifts. */
    --modal-ease:           cubic-bezier(0.32, 0.72, 0, 1);
    --modal-slide-duration: 0.36s;   /* slide-up (action / form sheets) */
    --modal-pop-duration:   0.22s;   /* pop (login / chooser dialogs) */
    --backdrop-duration:    0.3s;
    --backdrop-color:       rgba(0, 0, 0, 0.5);
}

/* ───────────────────────────────────────────
   BORDER-RADIUS SCALE — single source of truth
   (defined here so it's available site-wide).

   Use a token for every rounded surface instead of a raw px.
   The 6 step values absorb the old scattered radii:

        --radius-xs   8px    ← 8
        --radius-sm   12px   ← 10, 12
        --radius-md   16px   ← 14, 16   (cards & inner blocks)
        --radius-lg   20px   ← 20, 22
        --radius-xl   24px   ← 24, 25
        --radius-2xl  28px   ← 28, 30, 32 (bottom sheets)
        --radius-pill 999px  (capsule buttons / badges)
        --radius-circle 50%  (avatars / dots)

   Exceptions (intentional, NOT tokens): the planning-nav
   concentric set (23 / 29 / 50px) is a calculated relationship,
   and tiny structural values (1px, 2px handles, 6px) compete only
   within their own component.
   ─────────────────────────────────────────── */
:root {
    --radius-xs:     8px;
    --radius-sm:     12px;
    --radius-md:     16px;
    --radius-lg:     20px;
    --radius-xl:     24px;
    --radius-2xl:    28px;
    --radius-pill:   999px;
    --radius-circle: 50%;
}

/* ───────────────────────────────────────────
   Color scale (single source of truth)

   Airbnb-style neutral ramp + brand teal. Never hardcode a gray or
   teal for a normal surface/text — use a token. If a value doesn't
   fit, snap it to the nearest token; don't invent a magic hex.

   NOT tokenized (intentional): header colors, and the landing/region
   map-area grays. Most accent/semantic colors (coral #ff385c, danger
   #c0392b, amber, green, etc.) are out of scope for now and left raw.
   Exception: the downvote lavender is tokenized as --color-downvote
   (single, consistent, used in a few spots).

   For "slightly lighter on hover / darker on active" do NOT add a new
   token — derive from the base with color-mix():
       :hover  { background: color-mix(in srgb, var(--color-brand), #fff 12%); }
       :active { background: color-mix(in srgb, var(--color-brand), #000 10%); }
   (--color-brand-strong is a real swap color, not a hover shade.)

   Caching: when you change a token value, bump this file's ?v= on
   every page that links it (same rule as z-index / radius).
   ─────────────────────────────────────────── */
:root {
    /* Text (ink → light) */
    --color-text:           #222222;  /* body / headings (soft black) */
    --color-text-secondary: #484848;  /* mid-strength text */
    --color-text-muted:     #717171;  /* captions, secondary info */
    --color-text-faint:     #888888;  /* faint labels (#999 folded in) */
    --color-text-disabled:  #b0b0b0;  /* disabled / placeholder */
    /* Line / surface (light → dark) */
    --color-border:         #dddddd;  /* card / input borders */
    --color-divider:        #ebebeb;  /* dividers, faint fills */
    --color-surface:        #f7f7f7;  /* hover / secondary surface */
    --color-surface-alt:    #fafafa;  /* very faint surface */
    --color-surface-dark:   #1a1a1a;  /* dark sections */
    --color-bg:             #ffffff;  /* base background */
    /* Brand (teal) */
    --color-brand:          #23d3d3;  /* primary brand */
    --color-brand-strong:   #1ebcbc;  /* strong teal (text / hover swap) */
    --color-brand-dark:     #0f8c8c;  /* deep teal */
    --color-brand-tint:     #e6f7f7;  /* faint teal background */
    /* Special */
    --color-guru-report:    #333333;  /* Guru Report CTA / paywall only */
    --color-downvote:       #9494ff;  /* downvote state (lavender) — only */
    /* Semantic — each is a strong text color + a faint tint background */
    --color-danger:         #c0392b;  /* delete / closed / error text */
    --color-danger-tint:    #fdeded;  /* danger badge background */
    --color-warning:        #b87318;  /* pending / caution text */
    --color-warning-tint:   #fff4e1;  /* warning badge background */
    --color-success:        #357d2f;  /* open / success text */
    --color-success-tint:   #f0f9ee;  /* success badge background */
    --color-notification:   #ff385c;  /* unread dot / heart / alert (coral) */
}

/* ───────────────────────────────────────────
   Site header (shared across all pages)
   ─────────────────────────────────────────── */

/* Reserve the header's height on the empty #site-header wrapper so the
   layout below doesn't jump up while header.js is fetching header.html
   (visible as a ~0.5s flash on slower hosting). Values match .menu-container's
   rendered height: 72px on desktop (≥744), 56px on mobile (≤743). */
#site-header { min-height: 72px; }
@media (max-width: 743px) {
    #site-header { min-height: 56px; }
}

.menu-container {
    height: 72px; /* matches .hv2 .menu-container (header.html) + #site-header reserve */
    background-color: white;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.menu-inner {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    max-width: 1280px;
    margin: 0 auto;
}

.site-logo {
    position: absolute;
    left: 30px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    z-index: var(--z-header-actions);
}

.site-logo img {
    height: 36px;
    width: auto;
    display: block;
}

.header-actions {
    position: absolute;
    right: 30px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    gap: 10px;
    z-index: var(--z-header-actions);
}

.add-place-btn {
    height: 40px;
    /* Padding flexes with viewport so the pill button shrinks by sacrificing
       padding before the text would ever wrap to two lines. */
    padding: 0 clamp(10px, 2.4vw, 22px);
    white-space: nowrap;
    background-color: #23d3d3;
    color: white;
    border: 1px solid #23d3d3;
    border-radius: 999px;
    font-family: 'Outfit', Arial, sans-serif;
    font-size: 14px;
    font-weight: 400;
    cursor: pointer;
    text-decoration: none;
    box-sizing: border-box;
    transition: background-color 0.2s ease, color 0.2s ease;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.add-place-btn .plus-icon {
    width: 14px;
    height: 14px;
    display: inline-block;
}

/* Filled → outlined on hover. Border + icon + text all stay #23d3d3. */
.add-place-btn:hover {
    background-color: #ffffff;
    color: #23d3d3;
}

/* ───── Profile button (Airbnb-style) ───── */
.profile-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    width: 40px;
    height: 40px;
    background-color: white;
    border: 1px solid #dddddd;
    border-radius: 50%;
    cursor: pointer;
    font-family: 'Outfit', Arial, sans-serif;
    color: #222;
    transition: box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.profile-btn:hover {
    transform: scale(1.05);
    box-shadow: 0 0 3px rgba(0, 0, 0, 0);
    border-color: #6e6e6e;
}

.profile-avatar {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #6e6e6e;
}

.profile-avatar svg {
    width: 100%;
    height: 100%;
    display: block;
}

.profile-menu {
    position: relative;
    display: inline-flex;
}

.profile-dropdown {
    position: absolute;
    top: calc(100% + 12px);
    left: 50%;
    width: 170px;
    background-color: white;
    border-radius: var(--radius-md);
    box-shadow: 0 2px 16px rgba(0, 0, 0, 0.12);
    padding: 8px;
    opacity: 0;
    visibility: hidden;
    transform: translate(-50%, -6px);
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
    z-index: var(--z-header-overlay);
}

.profile-dropdown.open {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
}

.profile-dropdown-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 12px;
    border-radius: var(--radius-sm);
    color: #222;
    font-size: 14px;
    font-weight: 400;
    text-decoration: none;
    transition: background-color 0.15s ease;
}

.profile-dropdown-icon {
    width: 18px;
    height: 18px;
    color: #6e6e6e;
    flex-shrink: 0;
}

.profile-dropdown-item:hover {
    background-color: #f7f7f7;
}

.profile-dropdown-divider {
    height: 1px;
    background-color: #ebebeb;
    margin: 6px 8px;
}

/* "Staff" pill on the Dashboard shortcut — mirrors /profile's .profile-nav-badge
   (teal), sized down for the narrow (170px) header dropdown. */
.profile-dropdown-badge {
    margin-left: auto;
    background-color: var(--color-brand);
    color: #fff;
    font-size: 10px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: 0.02em;
    padding: 3px 7px;
    border-radius: var(--radius-pill);
}

.menu-wrapper {
    display: flex;
    align-items: center;
    gap: 40px;
}

.menu {
    display: flex;
    gap: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

.menu li {
    color: #777777;
    font-size: 16px;
    font-weight: 400;
    cursor: pointer;
    transition: color 0.3s ease;
    position: relative;
    padding: 20px 16px;
}

.menu li:hover {
    color: #23d3d3;
}

/* Top-level nav links (Travel / Beauty) — inherit color from parent <li>
   so hover still tints them while preserving the mega-menu hover behavior. */
.menu li .menu-link {
    color: inherit;
    text-decoration: none;
    font: inherit;
}

.mega-menu {
    position: fixed;
    top: 60px;
    left: 50%;
    transform: translateX(-50%);
    background-color: white;
    border-radius: var(--radius-xl);
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.5s ease, visibility 0.5s ease;
    z-index: var(--z-header);
}

.menu li:hover .mega-menu {
    opacity: 1;
    visibility: visible;
}

/* ───── Travel mega menu ───── */
.mega-menu--panel {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    /* Caps at 960; below 988px viewport, shrinks with 14px breathing
       room on each side (viewport - 28). Mobile mega-active overrides
       this with position:fixed left/right anyway. */
    width: calc(100vw - 28px);
    max-width: 960px;
    height: 375px;
    padding: 24px 32px;
    box-sizing: border-box;
    text-align: left;
}

/* Per-tab content pane. Only the .active pane shows. Pane itself is flex
   column so we can push By Category to the bottom inside it. */
.menu-pane {
    display: none;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.menu-pane.active {
    display: flex;
}

.menu-pane .by-category-title {
    margin-top: auto;
}

/* Accordion-style pane header — desktop hides it (tabs row at top handles
   pane switching); mobile mega-active reveals it as a tappable button. */
.menu-pane-header {
    display: none;
}

.menu-pane-placeholder {
    color: #888;
    font-size: 14px;
    padding: 8px 0;
}

/* Mobile-only two-view layout (main buttons + By Category sub-view).
   Hidden on desktop — desktop uses the .menu-cards + .category-list
   layout. Mobile mega-active flips visibility (see header.html inline). */
.menu-pane-views {
    display: none;
}

.menu-tabs {
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #eee;
    padding-bottom: 14px;
    margin-bottom: 18px;
}

.menu-tab-list {
    display: flex;
    gap: 36px;
    flex-wrap: nowrap;
    /* Horizontal swipe when tabs overflow (e.g. 8 Travel tabs at 744px).
       Vertical scroll is blocked via overflow-y:hidden + touch-action:pan-x.
       The padding/negative-margin pair reserves room so the active
       underline (::after at bottom:-15px) isn't clipped by overflow-y. */
    overflow-x: auto;
    overflow-y: hidden;
    touch-action: pan-x;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 18px;
    margin-bottom: -18px;
    flex: 1;
    min-width: 0;
}
.menu-tab-list::-webkit-scrollbar { display: none; }

.menu-tab {
    position: relative;
    flex-shrink: 0;
    white-space: nowrap;
    color: #999;
    font-size: 15px;
    font-weight: 400;
    cursor: pointer;
    transition: color 0.2s ease;
}

/* Plain text + icon link that replaces the "Overview" tab — navigates to the
   Travel landing page instead of switching panes. Styled like the text tabs. */
.menu-tab-cta {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    gap: 5px;
    white-space: nowrap;
    font-size: 15px;
    font-weight: 400;
    color: #999;
    text-decoration: none;
    transition: color 0.2s ease;
}
.menu-tab-cta:hover {
    color: #23d3d3;
}
.menu-tab-cta-icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
}

/* Enlarge the clickable area WITHOUT changing the visual size: a transparent
   ::before overlay extends the hit target beyond the glyphs (12px sides,
   10px top/bottom) while the tab box itself stays text-sized, so spacing and
   the active underline are unchanged. */
.menu-tab::before {
    content: '';
    position: absolute;
    top: -10px;
    bottom: -10px;
    left: -12px;
    right: -12px;
}

.menu-tab:hover {
    color: #23d3d3;
}

.menu-tab.active {
    color: #111;
    font-weight: 500;
}

/* Teal indicator bar sits on the gray border line of .menu-tabs
   (parent has padding-bottom: 14px + border-bottom: 1px), so bottom: -15px
   with height: 2px overlaps the gray line at the active tab's x-range. */
.menu-tab.active::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -15px;
    height: 2px;
    background-color: #23d3d3;
    border-radius: 1px;
}

.menu-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
    margin: 0 -14px 22px;
}

.menu-card {
    position: relative;
    /* Fixed height so the card stays the same size as the mega-menu
       width shrinks toward 761px — only the card width follows the
       grid column. (Was aspect-ratio 16/10, which also shrunk height.) */
    height: 186px;
    border-radius: var(--radius-lg);
    /* Loading placeholder (photo cards only — .menu-card--flat overrides it).
       Safe as a pale gray even though the title is white: the injected black
       gradient is a CSS layer, so it paints immediately while the Cloudinary
       image is still in flight. ⚠️ It only paints on cards that HAVE a photo
       assigned (the injector bails on an empty slot) — if a header slot is ever
       left unassigned, its white title would sit on this bare gray. All 18 photo
       slots are assigned today; revisit this colour if that changes. */
    background: #f0f0f0;
    overflow: hidden;
    cursor: pointer;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.15);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

/* Bevel — same values as the place-card V2 image (place-card.css .place-card--v2
   .place-card-img::after; keep the two in sync): top inner edge catches light,
   bottom inner edge shades, plus a 1px hairline keyline that replaces the removed
   border. On these dark cards the bottom shade is barely visible — intentional,
   the effect should read as "barely raised", not skeuomorphic. pointer-events:none
   so it never blocks clicks; z-index:3 paints above the bg image + text overlay
   (translucent, so text shows through). */
.menu-card::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    z-index: 3;
    box-shadow:
        inset 0 2px 2px rgba(255, 255, 255, 0.55),
        inset 0 -2px 3px rgba(0, 0, 0, 0.22),
        inset 0 0 0 1px rgba(0, 0, 0, 0.08);
}

.menu-card:hover {
    transform: scale(1.02);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}

/* Flat-card variant — a light card that stands in for a photo card: flat white
   with a gray border that lifts + gains a shadow on hover (mirrors the detail-
   page "Featured in" button .nearby-link), NOT the photo cards' bevel. Dark text.
   No data-slot → no Cloudinary photo, no load delay. Used by:
     - Interests collections (Photography / Cherry Blossoms / KPop Demon Hunters)
       WITH a theme SVG icon top-left (.menu-card-icon) — sidesteps IP/copyright.
     - Beauty Concerns (Wrinkles / Spots / Acne) — text-only, no icon. */
.menu-card--flat {
    box-sizing: border-box;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    box-shadow: none;
}
.menu-card--flat::after { content: none; }   /* flat — no bevel */
.menu-card--flat:hover {
    border-color: color-mix(in srgb, var(--color-border), #000 20%);
    transform: scale(1.02);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.menu-card--flat .menu-card-overlay {
    color: var(--color-text);
}
.menu-card-icon {
    position: absolute;
    top: 18px;
    left: 18px;
    width: 48px;
    height: 48px;
    color: #9e9e9e;
    z-index: 2;
}

.menu-card-overlay {
    position: absolute;
    left: 18px;
    bottom: 17px;
    color: #fff;
}

.menu-card-title {
    font-size: 22px;
    font-weight: 500;
    line-height: 1.1;
}

/* Optional card subtitle — kept for the mobile pane-builder fallback even
   though no card currently uses it. */
.menu-card-sub {
    font-size: 14px;
    font-weight: 400;
    color: rgba(255, 255, 255, 0.85);
}

.by-category-title {
    font-size: 16px;
    font-weight: 500;
    color: #111;
    margin-bottom: 12px;
}

/* Subtitle lists (By Categories / More X) sit in a fixed 5-column grid so the
   column pitch stays constant no matter how many buttons there are — that's
   what keeps the spacing from ballooning when a list has only 3 vs 5 items.
   Buttons fill columns left-to-right, each left-aligned in its column; fewer
   than 5 simply leave the right-hand columns empty (a list holds up to 5). */
.category-list {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    justify-items: start;
    gap: 16px;
}

.category-list a {
    color: #555;
    font-size: 14px;
    font-weight: 400;
    text-decoration: none;
    transition: color 0.2s ease;
}

.category-list a:hover:not(.menu-locked) {
    color: #23d3d3;
}

/* Locked (not-yet-live) menu/search links — "Disabled = hover OFF" (CLAUDE.md):
   hover rules are gated with :not(.menu-locked); no pointer-events:none so the
   title="Coming soon" tooltip and the not-allowed cursor still work. Locked
   <a>s carry no href, so they never navigate. */
.menu-locked {
    opacity: 0.4;
    cursor: not-allowed;
}

.community-links {
    display: flex;
    gap: 56px;
}

.community-links a {
    color: #555;
    font-size: 14px;
    font-weight: 400;
    text-decoration: none;
    transition: color 0.2s ease;
}

.community-links a:hover {
    color: #23d3d3;
}

.search-icon {
    width: 18px;
    height: 18px;
    cursor: pointer;
    transition: all 0.3s ease;
    color: #777777;
    position: relative;
    z-index: var(--z-header-overlay);
}

.search-icon:hover {
    color: #23d3d3;
    transform: scale(1.1);
}

/* ───────────────────────────────────────────
   Mobile body scroll lock — when the v2 mobile
   header is in any expanded state (search-active
   inset bar, mega-active section, or profile
   dropdown panel), prevent the page from
   scrolling underneath so the dimmed body
   content stays fixed.
   ─────────────────────────────────────────── */
@media (max-width: 743px) {
    body:has(.hv2 .menu-wrapper.search-active),
    body:has(.hv2 .menu-inner.profile-active) {
        overflow: hidden;
    }
}

/* ───────────────────────────────────────────
   FilterChips — shared hierarchical filter-chip
   component (markup built by FilterChips() in
   header.js). A pill chip, optionally with a
   concentric caret that opens a sub-type menu.
   Geometry matches the /t/ map-page filter chips:
   32px chip → 16px cap radius; 26px caret circle
   (13px radius); padding-right 2px puts the caret
   right edge concentric with the cap (16−13 = 3px
   ring = 2px inset + 1px border).
   ─────────────────────────────────────────── */
.fchip {
    height: 32px;
    box-sizing: border-box;
    padding: 0 14px;
    display: inline-flex;
    align-items: center;
    line-height: 1;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-bg);
    font-family: inherit;
    font-size: 13px;
    color: var(--color-text);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}
.fchip:hover { border-color: var(--color-text); }
.fchip.active {
    background: var(--color-surface-dark);
    color: var(--color-bg);
    border-color: var(--color-text);
}

.fchip-dropdown { position: relative; display: inline-flex; }
.fchip--dropdown { gap: 0; padding-right: 2px; }
.fchip-label {
    display: inline-flex;
    align-items: center;
    padding-right: 2px;
}
.fchip-caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    border-radius: var(--radius-circle);
    transition: background-color 0.12s ease;
}
.fchip-caret:hover { background: rgba(0, 0, 0, 0.08); }
.fchip--dropdown.active .fchip-caret:hover { background: rgba(255, 255, 255, 0.2); }
.fchip-caret svg { width: 8px; height: 5px; fill: currentColor; }

.fchip-menu {
    display: none;
    /* Fixed + JS-positioned (FilterChips.positionMenu) so the menu escapes the
       horizontally-scrolling chip bar's overflow clipping. */
    position: fixed;
    z-index: 1300;
    flex-direction: column;
    min-width: 180px;
    padding: 6px;
    background: var(--color-bg);
    border: 1px solid var(--color-divider);
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}
.fchip-menu--open { display: flex; }
.fchip-menu button {
    appearance: none;
    border: none;
    background: transparent;
    text-align: left;
    padding: 8px 12px;
    border-radius: var(--radius-xs);
    font-family: inherit;
    font-size: 13px;
    color: var(--color-text);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.12s ease;
}
.fchip-menu button:hover { background: var(--color-surface); }
.fchip-menu button.active { background: var(--color-surface-dark); color: var(--color-bg); }

/* ───────────────────────────────────────────
   Nearby / type button — a discovery card with the icon top-left and the
   label + chevron bottom-right. Shared component used by the /p/ detail
   "Nearby" & "Featured in" sections and the header search browse tiles.
   Container grids + the sidebar variant stay page-local; only the card
   itself (and its themed variant) live here as the single source of truth.
   ─────────────────────────────────────────── */
/* 2×2 CSS grid (not flexbox): icon spans the top row; the label and the chevron
   each get their OWN bottom-row cell. Keeping the chevron in a separate cell
   (rather than nested in the label) pins it to the bottom-right corner even
   when the label wraps to two lines — so its position never shifts between
   buttons. The chevron must therefore be a sibling of .nearby-text in markup. */
.nearby-link {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto;          /* label | chevron */
    grid-template-rows: auto minmax(0, 1fr);  /* icon row | label+chevron row */
    column-gap: 6px;
    box-sizing: border-box;
    height: 60px;
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-text);
    font-family: inherit;
    font-size: 14px;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.nearby-link:hover:not(.menu-locked) {
    border-color: color-mix(in srgb, var(--color-border), #000 20%);
    transform: scale(1.03);
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.nearby-icon {
    width: 20px;
    height: 20px;
    color: var(--color-text-disabled);
    grid-column: 1 / span 2;
    grid-row: 1;
    align-self: start;
    justify-self: start;
}
.nearby-text {
    grid-column: 1;
    grid-row: 2;
    align-self: end;
    justify-self: end;
    text-align: right;
}
.nearby-chevron {
    grid-column: 2;
    grid-row: 2;
    align-self: end;
    width: 13px;
    height: 13px;
    stroke-width: 2;
    color: var(--color-text-faint);
    /* Nudge up 1px to align the chevron's visual center with the text's cap-height midline. */
    transform: translateY(-1px);
}

/* Themed variant (taller card, big 36px icon). Used by /p/ "Featured in"
   (.themed-maps-list) and the search Interests group (.search-browse-grid--themed).
   Inherits the grid layout above; only the size/proportions differ. */
.themed-maps-list .nearby-link,
.search-browse-grid--themed .nearby-link {
    height: auto;
    aspect-ratio: 1.618 / 1;
    grid-template-rows: 1fr 1fr;
    gap: 4px 6px;
    padding-right: 8px;
}
.themed-maps-list .nearby-icon,
.search-browse-grid--themed .nearby-icon {
    width: 36px;
    height: 36px;
}

/* ═══════════════ Modal / overlay animation — shared scaffold ═══════════════
   EVERY centred modal uses this .ktg-modal scaffold (loaded site-wide here) —
   the same single-source approach as .close-btn. Position · the open/close
   (visibility) mechanism · backdrop fade · panel motion live ONLY here. A
   modal's own class keeps just its look (width / padding / content).

   Two panel variants (add ONE to the panel element):
     --slide   action / form sheets   (profile setup; detail-page sheet modals)
     --pop     login / chooser dialogs (auth login, role chooser)
   Open/close by toggling .is-open on the .ktg-modal. See CLAUDE.md. */
.ktg-modal {
    position: fixed;
    inset: 0;
    z-index: var(--z-overlay-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: hidden;
    pointer-events: none;
    /* stay rendered until the panel finishes its exit, then hide */
    transition: visibility 0s linear var(--modal-slide-duration);
}
.ktg-modal.is-open {
    visibility: visible;
    pointer-events: auto;
    transition: visibility 0s linear 0s;
}
.ktg-modal__backdrop {
    position: absolute;
    inset: 0;
    background: var(--backdrop-color);
    z-index: var(--z-overlay-backdrop);
    opacity: 0;
    transition: opacity var(--backdrop-duration) ease;
}
.ktg-modal.is-open .ktg-modal__backdrop { opacity: 1; }
.ktg-modal__panel {
    position: relative;
    z-index: var(--z-overlay-modal);
}
/* slide-up: from below → centre */
.ktg-modal__panel--slide {
    transform: translateY(100vh);
    transition: transform var(--modal-slide-duration) var(--modal-ease);
}
.ktg-modal.is-open .ktg-modal__panel--slide { transform: translateY(0); }
/* pop: small rise + scale + fade */
.ktg-modal__panel--pop {
    opacity: 0;
    transform: translateY(8px) scale(0.98);
    transition: opacity var(--modal-pop-duration) ease, transform var(--modal-pop-duration) ease;
}
.ktg-modal.is-open .ktg-modal__panel--pop { opacity: 1; transform: none; }

/* ═══════════════ Login / sign-up modal (Airbnb-style) ═══════════════
   Layout + open/close motion come from the .ktg-modal scaffold above
   (.ktg-modal · .ktg-modal__backdrop · .ktg-modal__panel--pop on the markup);
   the rules here are only the dialog's own look. */
.auth-dialog {
    width: 100%;
    max-width: 440px;
    margin: 16px;
    background: #fff;
    border-radius: var(--radius-2xl);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
    overflow: hidden;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 32px);
}

/* ── Close button (X) — shared single source of truth ───────────────────
   EVERY modal/sheet close button uses .close-btn (loaded site-wide here).
   Size · colour · hover · shape live ONLY in this block — do not redeclare
   them on per-component close classes. See CLAUDE.md "Close button (X)".
     one size           36px circle, 18px icon   → ALL modals & sheets
     .close-btn--corner absolute, pinned top-right 10/10, concentric with a
                        var(--radius-2xl)=28px corner (10 + 18 = 28)
   Responsive sheets keep their own display:none↔flex rules (loaded later,
   so they win over the inline-flex default). */
.close-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-secondary);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.close-btn:hover { background: var(--color-divider); color: var(--color-text); }
.close-btn svg { width: 18px; height: 18px; }
/* Concentric with the panel's 28px corner: 10 + 18 (button radius) = 28 */
.close-btn--corner { position: absolute; top: 10px; right: 10px; z-index: 1; }

/* Long-form prose `.prose` = `partials/prose.css` (상단 @import). SSOT·스펙: docs/prose.md */

.auth-body { padding: 24px 24px 32px; overflow-y: auto; }
@media (max-width: 480px) { .auth-body { padding: 20px 16px 28px; } }

.auth-logo {
    display: block;
    height: 48px;
    width: auto;
    margin: 12px auto 24px;
}
.auth-title {
    font-size: 24px;
    font-weight: 600;
    color: #222;
    text-align: center;
    margin: 8px 0 28px;
    line-height: 1.25;
}

.auth-form { display: flex; flex-direction: column; }
.auth-field { position: relative; }
.auth-input {
    width: 100%;
    box-sizing: border-box;
    height: 60px;
    padding: 24px 16px 8px;
    border: 1px solid #b0b0b0;
    border-radius: 12px;
    font: 16px/1.2 'Outfit', Arial, sans-serif;
    color: #222;
    transition: border-color 0.15s ease;
}
.auth-input::placeholder { color: transparent; }
.auth-input:hover { border-color: color-mix(in srgb, var(--color-border), #000 20%); }
.auth-input:focus { outline: none; border-color: var(--color-text); }

/* Floating label: centred placeholder → shrinks to the top line on focus/fill */
.auth-field-label {
    position: absolute;
    left: 17px;
    top: 19px;
    color: #717171;
    font-size: 16px;
    pointer-events: none;
    transition: top 0.15s ease, font-size 0.15s ease;
}
.auth-input:focus ~ .auth-field-label,
.auth-input:not(:placeholder-shown) ~ .auth-field-label {
    top: 9px;
    font-size: 12px;
}

/* Demo-only hint below the input — appears while the field is focused */
.auth-demo-hint {
    margin: 0 4px;
    font-size: 13px;
    color: #717171;
    line-height: 1.4;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transition: max-height 0.18s ease, opacity 0.18s ease, margin-top 0.18s ease;
}
.auth-field:focus-within ~ .auth-demo-hint {
    max-height: 44px;
    opacity: 1;
    margin-top: 8px;
}
.auth-continue {
    width: 100%;
    height: 48px;
    margin-top: 16px;
    padding: 0 16px;
    border: none;
    border-radius: 12px;
    background: #23d3d3;
    color: #fff;
    font: 500 16px 'Outfit', Arial, sans-serif;
    cursor: pointer;
    transition: filter 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.auth-continue:hover {
    filter: brightness(1.06);
    transform: scale(1.01);
    box-shadow: 0 4px 14px rgba(35, 211, 211, 0.3);
}
.auth-continue:active {
    transform: scale(0.99);
    box-shadow: 0 1px 2px rgba(35, 211, 211, 0.25);
}

.auth-or {
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 22px 0;
    color: #222;
    font-size: 13px;
}
.auth-or::before, .auth-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: #ddd;
}

.auth-socials { display: flex; justify-content: center; gap: 12px; }
.auth-social {
    width: 48px;
    height: 48px;
    border: 1px solid var(--color-border);
    border-radius: 12px;
    background: #fff;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.auth-social:hover { border-color: #717171; }
.auth-social svg { width: 22px; height: 22px; }

body.auth-open { overflow: hidden; }
