/* =============================================
   ALCHEMIST — Global Styles
   Fully responsive: mobile-first, all screen sizes
   ============================================= */

/* Custom fonts */
@font-face {
    font-family: 'Gotham';
    src: url('fonts/GothamLight.otf') format('opentype');
    font-weight: 300;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Gotham';
    src: url('fonts/GothamBook.otf') format('opentype');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Gotham';
    src: url('fonts/ufonts.com_gotham-medium.ttf') format('truetype');
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

/* Reset & Base */
*, *::before, *::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

html {
    font-size: 16px;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: 'Gotham', sans-serif;
    font-weight: 300;
    color: #f5f0e8;
    background: #1a1714;
    overflow: hidden;
    /* dvh handles mobile browser chrome (address bar) */
    height: 100dvh;
    height: 100vh; /* fallback */
    width: 100vw;
    /* Prevent overscroll bounce on iOS */
    overscroll-behavior: none;
    touch-action: manipulation;
}

a {
    color: inherit;
    text-decoration: none;
}

/* =============================================
   HERO SECTION
   ============================================= */

.hero {
    position: relative;
    width: 100vw;
    height: 100dvh;
    height: 100vh; /* fallback */
    overflow: hidden;
    background: #1a1714;
}

/* =============================================
   SLIDESHOW — stacked full-screen images
   ============================================= */

.slideshow {
    position: absolute;
    inset: 0;
    z-index: 0;
}

.slideshow__slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    opacity: 0;
    transition: opacity 1.6s ease-in-out;
    will-change: opacity;
}

.slideshow__slide--active {
    opacity: 1;
}

/* =============================================
   CLIENT BACKGROUND — shown on client hover/tap
   ============================================= */

.client-bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    opacity: 0;
    transition: opacity 0.6s ease-in-out;
    pointer-events: none;
}

.client-bg--visible {
    opacity: 1;
    z-index: 1;
}

.client-bg__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    opacity: 0;
    transition: opacity 0.6s ease-in-out;
    will-change: opacity;
}

.client-bg__img--visible {
    opacity: 1;
}

/* =============================================
   OVERLAY
   ============================================= */

.hero__overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(
        105deg,
        rgba(18, 15, 12, 0.55) 0%,
        rgba(18, 15, 12, 0.30) 35%,
        rgba(18, 15, 12, 0.08) 65%,
        rgba(18, 15, 12, 0.02) 100%
    );
    z-index: 2;
    pointer-events: none;
}

/* =============================================
   HERO CONTENT
   ============================================= */

.hero__content {
    position: absolute;
    left: 20px;
    top: 24px;
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: 24px;
    opacity: 0;
    animation: fadeInContent 1.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.3s forwards;
    /* Prevent content from overflowing the viewport */
    max-height: calc(100dvh - 48px);
    max-height: calc(100vh - 48px); /* fallback */
    overflow-y: auto;
    overflow-x: hidden;
    /* Hide scrollbar but allow scrolling */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.hero__content::-webkit-scrollbar {
    display: none;
}

/* Logo / Brand Name */
/* Wordmark — the EXACT face of the logo artwork (Cousine, the
   Liberation-Mono design: flag-serif i/l with foot bars). Lowercase
   to match the logo lock-up. Monospace glyphs are already wide, so
   tracking is a restrained 0.12em everywhere — the logo itself uses
   ~0; 0.25em+ makes the word fall apart at this width. */
.hero__logo {
    font-family: 'Cousine', monospace;
    font-weight: 400;
    font-size: 1.2rem;
    letter-spacing: 0.12em;
    color: #f5f0e8;
    text-transform: lowercase;
    line-height: 1;
    text-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
}

/* Navigation */
.hero__nav {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* Room for the LAST link's hover underline (drawn at bottom:-2px
       on desktop) — without this the hero content's scroll container
       clips it, so e.g. "About" never showed its line. */
    padding-bottom: 4px;
}

.hero__nav-link {
    font-family: 'Gotham', sans-serif;
    font-weight: 400;
    font-size: 0.94rem;
    letter-spacing: 0.04em;
    color: rgba(245, 240, 232, 0.88);
    transition: color 0.35s ease, transform 0.35s ease;
    display: inline-block;
    position: relative;
    width: fit-content;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
    cursor: pointer;
    /* Touch-friendly minimum tap target */
    min-height: 44px;
    line-height: 44px;
}

.hero__nav-link::after {
    content: '';
    position: absolute;
    bottom: 10px;
    left: 0;
    width: 0;
    height: 1px;
    background: rgba(245, 240, 232, 0.7);
    transition: width 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.hero__nav-link:hover {
    color: #ffffff;
    transform: translateX(4px);
}

.hero__nav-link:hover::after {
    width: 100%;
}

/* Same underline on keyboard focus and on tap (touch devices have
   no hover) — every header, including content sections, gets it */
.hero__nav-link:focus-visible,
.hero__nav-link:active {
    color: #ffffff;
}

.hero__nav-link:focus-visible::after,
.hero__nav-link:active::after {
    width: 100%;
}

/* =============================================
   CORNER FLASK — persistent brand mark
   Sits quietly in the bottom-right of EVERY page,
   mirroring the wordmark's top-left placement.
   Rendered via CSS mask so it is always a solid
   silhouette in the wordmark cream on a fully
   transparent background. Decorative only —
   pointer-events off so it never blocks the UI.
   ============================================= */

.corner-flask {
    position: fixed;
    right: 20px;
    bottom: 24px;
    z-index: 3;
    width: 34px;
    aspect-ratio: 150 / 219; /* intrinsic ratio of the flask art */
    /* The ACTUAL logo artwork (gradient + shading), recoloured to
       white/silver with a filter — not a flat masked silhouette.
       The PNG has real alpha, so the background stays transparent.
       intro.js reads this filter to recolour the gliding flask. */
    background: url('assets/logo-flask.png') center / contain no-repeat;
    filter: grayscale(1) brightness(1.55);
    opacity: 0.85;
    pointer-events: none;
}

/* =============================================
   SLIDE CAPTION — a small description of the
   current slideshow image, pinned bottom-left.
   Matches the nav headers (.hero__nav-link) in
   font, size, colour and left offset at every
   breakpoint, and crossfades with each slide.
   Hidden during the intro and while a client-
   hover background is shown.
   ============================================= */
.slide-caption {
    position: fixed;
    left: 20px;
    bottom: 24px;
    z-index: 3;
    max-width: min(66vw, 560px);
    margin: 0;
    font-family: 'Gotham', sans-serif;
    font-weight: 400;
    font-size: 0.94rem;
    letter-spacing: 0.04em;
    line-height: 1.4;
    color: rgba(245, 240, 232, 0.88);
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.2);
    pointer-events: none;
    /* Pure opacity fade so it crossfades with the slideshow image
       (out with the old slide, in with the new — ~0.8s each way to
       span the image's 1.6s crossfade). */
    opacity: 0;
    transition: opacity 0.8s ease-in-out;
}

.slide-caption--show {
    opacity: 1;
}

/* Hidden outright while the intro runs (revealed by main.js after). */
html.intro-active .slide-caption {
    opacity: 0 !important;
}

/* =============================================
   NAV GROUP — wrapper for link + dropdown
   ============================================= */

.nav-group {
    display: flex;
    flex-direction: column;
    gap: 0;
}

/* =============================================
   DROPDOWN
   ============================================= */

.dropdown {
    list-style: none;
    padding: 0;
    margin: 0;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    transition: max-height 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94),
                opacity 0.4s ease,
                margin-top 0.4s ease;
    margin-top: 0;
}

.dropdown--open {
    max-height: 400px;
    opacity: 1;
    margin-top: 4px;
}

/* Staggered reveal — items appear one by one when the list opens.
   Each li's --i index is set in JS; closing resets instantly. */
.dropdown li {
    opacity: 0;
    transform: translateY(-6px);
    transition: opacity 0.35s ease, transform 0.35s ease;
}

.dropdown--open li {
    opacity: 1;
    transform: translateY(0);
    transition-delay: calc(var(--i, 0) * 80ms);
}

.dropdown__item {
    font-family: 'Gotham', sans-serif;
    font-weight: 400;
    font-size: 0.9rem;
    letter-spacing: 0.02em;
    color: rgba(245, 240, 232, 0.65);
    padding: 0 0 0 16px;
    display: block;
    position: relative;
    transition: color 0.3s ease, transform 0.3s ease, padding-left 0.3s ease;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
    cursor: pointer;
    /* Touch-friendly tap target */
    min-height: 44px;
    line-height: 44px;
}

.dropdown__item::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 6px;
    height: 1px;
    background: rgba(245, 240, 232, 0);
    transition: background 0.3s ease, width 0.3s ease;
}

/* Active state for touch devices */
.dropdown__item:hover,
.dropdown__item--active {
    color: #ffffff;
    padding-left: 22px;
}

.dropdown__item:hover::before,
.dropdown__item--active::before {
    background: rgba(245, 240, 232, 0.7);
    width: 10px;
}

/* "Others" row (e.g. FULL PROJECT LIST) — pinned at the bottom of a
   client drop-down, set apart by a line ABOVE it. The row is a NORMAL
   flush item (it keeps .dropdown__item's left padding at every
   breakpoint, so its text lines up with the client names). The line
   rides on the inner span so it spans exactly the label and sits
   flush-left with it. */
.dropdown__item--others {
    margin-top: 8px;
}

.dropdown__others-inner {
    display: inline-block;
    line-height: 1.4;
    padding-top: 12px;
    border-top: 1px solid rgba(245, 240, 232, 0.4);
}

/* Keep the row + its line steady on hover (no tick, no indent slide).
   The higher-specificity selector wins over the ≥900px media query
   that otherwise pushes hovered items to padding-left: 22px. */
.dropdown .dropdown__item--others:hover,
.dropdown .dropdown__item--others.dropdown__item--active {
    padding-left: 16px;
}

.dropdown__item--others::before {
    display: none;
}

/* Menu items WITHOUT a link (e.g. a phone number shown as text):
   plain text — no pointer, no hover slide */
.dropdown__item--static {
    cursor: default;
}

.dropdown__item--static:hover {
    color: rgba(245, 240, 232, 0.65);
    padding-left: 16px;
}

.dropdown__item--static:hover::before {
    background: rgba(245, 240, 232, 0);
    width: 6px;
}

/* Copy-to-clipboard button on phone / email menu items. Icon sits a
   touch dimmer than the text; goes brand-gold on hover and while
   showing the "copied" checkmark. The button box is bigger than the
   glyph so it's comfortably tappable on phones. */
.copy-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    vertical-align: middle;
    width: 30px;
    height: 30px;
    margin-left: 4px;
    padding: 0;
    background: none;
    border: none;
    color: rgba(245, 240, 232, 0.45);
    cursor: pointer;
    transition: color 0.3s ease;
}

.copy-btn svg {
    width: 0.85em;
    height: 0.85em;
    display: block;
}

.copy-btn:hover,
.copy-btn:focus-visible {
    color: #D8CA9D;
}

.copy-btn--done {
    color: #D8CA9D;
}

/* =============================================
   ANIMATIONS
   ============================================= */

@keyframes fadeInContent {
    0% {
        opacity: 0;
        transform: translateY(12px);
    }
    100% {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Reduce motion for users who prefer it */
@media (prefers-reduced-motion: reduce) {
    .slideshow__slide {
        transition: opacity 0.4s ease;
    }

    .client-bg {
        transition: opacity 0.2s ease;
    }

    .client-bg__img {
        transition: opacity 0.2s ease;
    }

    .hero__content {
        animation-duration: 0.4s;
    }

    .dropdown {
        transition-duration: 0.2s;
    }

    .dropdown li {
        transition-duration: 0.1s;
    }

    .dropdown--open li {
        transition-delay: 0s;
    }
}

/* =============================================
   RESPONSIVE — Mobile-first breakpoints
   ============================================= */

/* ---- Small phones (≤ 374px) ---- */
@media (max-width: 374px) {
    .hero__content {
        left: 14px;
        top: 16px;
        gap: 16px;
        max-height: calc(100dvh - 32px);
    }

    .hero__logo {
        font-size: 0.98rem;
        letter-spacing: 0.1em;
    }

    .hero__nav-link {
        font-size: 0.9rem;
        min-height: 40px;
        line-height: 40px;
    }

    .dropdown__item {
        font-size: 0.84rem;
        min-height: 40px;
        line-height: 40px;
    }

    .corner-flask {
        right: 14px;
        bottom: 16px;
        width: 30px;
    }

    .slide-caption {
        left: 14px;
        bottom: 16px;
        font-size: 0.9rem;
    }
}

/* ---- Tablets portrait (≥ 600px) ---- */
@media (min-width: 600px) {
    .hero__content {
        left: 32px;
        top: 32px;
        gap: 28px;
    }

    .slide-caption {
        left: 32px;
        bottom: 32px;
        font-size: 0.96rem;
    }

    .hero__logo {
        font-size: 1.25rem;
    }

    .hero__nav {
        gap: 2px;
    }

    .hero__nav-link {
        font-size: 0.96rem;
        min-height: 40px;
        line-height: 40px;
    }

    .dropdown__item {
        font-size: 0.9rem;
        min-height: 38px;
        line-height: 38px;
    }

    .corner-flask {
        right: 32px;
        bottom: 32px;
        width: 38px;
    }
}

/* ---- Tablets landscape / small desktops (≥ 900px) ---- */
@media (min-width: 900px) {
    .hero__content {
        left: clamp(40px, 4vw, 64px);
        top: clamp(36px, 5vh, 60px);
        gap: 32px;
    }

    .slide-caption {
        left: clamp(40px, 4vw, 64px);
        bottom: clamp(36px, 5vh, 60px);
        font-size: clamp(0.92rem, 1.05vw, 1rem);
    }

    .hero__logo {
        font-size: clamp(1.25rem, 1.6vw, 1.55rem);
    }

    .hero__nav-link {
        font-size: clamp(0.92rem, 1.05vw, 1rem);
        /* Hover interactions are reliable on desktop — relax tap target */
        min-height: unset;
        line-height: 1.8;
    }

    .hero__nav-link::after {
        bottom: -2px;
    }

    .dropdown__item {
        font-size: clamp(0.82rem, 0.9vw, 0.9rem);
        min-height: unset;
        line-height: 2;
        padding: 2px 0 2px 16px;
    }

    .dropdown__item:hover,
    .dropdown__item--active {
        padding-left: 22px;
    }

    .corner-flask {
        right: clamp(40px, 4vw, 64px);
        bottom: clamp(36px, 5vh, 60px);
        width: clamp(38px, 3vw, 46px);
    }
}

/* ---- Large desktops (≥ 1440px) ---- */
@media (min-width: 1440px) {
    .hero__content {
        left: 72px;
        top: 64px;
        gap: 36px;
    }

    .slide-caption {
        left: 72px;
        bottom: 64px;
        font-size: 1.02rem;
    }

    .hero__logo {
        font-size: 1.6rem;
    }

    .hero__nav-link {
        font-size: 1.02rem;
    }

    .dropdown__item {
        font-size: 0.92rem;
    }

    .corner-flask {
        right: 72px;
        bottom: 64px;
        width: 48px;
    }
}

/* ---- Ultrawide (≥ 2000px) ---- */
@media (min-width: 2000px) {
    .hero__content {
        left: 96px;
        top: 80px;
        gap: 40px;
    }

    .slide-caption {
        left: 96px;
        bottom: 80px;
        font-size: 1.12rem;
    }

    .hero__logo {
        font-size: 1.85rem;
    }

    .hero__nav-link {
        font-size: 1.12rem;
    }

    .dropdown__item {
        font-size: 1rem;
    }

    .corner-flask {
        right: 96px;
        bottom: 80px;
        width: 54px;
    }
}

/* =============================================
   INTRO ANIMATION
   Plays on every page load (skipped entirely for
   prefers-reduced-motion). Timeline (~5s):
     0.0s  grey brand colour fills the screen
     0.5s  flask logo slams into the centre
     1.5s  ALCHEMIST + 金丰寸 gently fade in
     3.3s  Chinese/background fade away while
           ALCHEMIST glides to its hero position
           and the flask shrinks + recolours down
           to the bottom-right corner mark
     5.0s  intro removed — site fully interactive
   Orchestrated by intro.js.
   ============================================= */

/* Grey immediately, before anything paints — no flash */
html.intro-active {
    background: #4D4D4F;
}

/* Without .intro-active (reduced motion), no intro at all */
.intro {
    display: none;
}

html.intro-active .intro {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 9999;
    overflow: hidden; /* the travelling wordmark never spills a scrollbar */
}

.intro__backdrop {
    position: absolute;
    inset: 0;
    background: #4D4D4F;
    will-change: opacity;
}

.intro__stack {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: clamp(18px, 3.2vh, 30px);
}

/* --- Flask logo: slam entrance ---
   The entrance animations are gated behind the JS-added
   `intro-play` class (added after first paint via rAF) instead
   of running straight from CSS. Some browsers (seen in Opera)
   fail to start load-time CSS animations until a reflow — the
   symptom was an invisible intro that only appeared on resize.
   Kicking the animations off from JS after layout has settled
   makes them fire reliably everywhere. */
.intro__flask {
    /* Height-aware so it never crowds short/landscape screens */
    width: min(40vw, 34vh, 150px);
    aspect-ratio: 150 / 219; /* intrinsic ratio of the flask art */
    /* The EXACT logo artwork — gold gradient and shading intact
       (not a masked flat silhouette). During the glide intro.js
       transitions `filter` to the corner mark's silver, so the
       shading survives the recolour. The neutral filter here gives
       the transition a matching start point to interpolate from. */
    background: url('assets/logo-flask.png') center / contain no-repeat;
    filter: grayscale(0) brightness(1);
    /* invisible until the slam; the oversized start lives only
       in the keyframes so nothing can ever snap back to it */
    opacity: 0;
    will-change: transform, opacity, filter;
    user-select: none;
    -webkit-user-select: none;
}

html.intro-play .intro__flask {
    animation: introSlam 0.6s cubic-bezier(0.22, 1, 0.36, 1) 0.5s forwards;
}

@keyframes introSlam {
    0%   { opacity: 0; transform: scale(2.8); }
    55%  { opacity: 1; transform: scale(0.96); }
    78%  { transform: scale(1.035); }
    100% { opacity: 1; transform: scale(1); }
}

/* --- Wordmark: same font as the hero logo.
       Font-size / letter-spacing are copied from the real
       hero logo at runtime by intro.js so the size is
       pixel-identical at every breakpoint. --- */
.intro__title {
    font-family: 'Cousine', monospace;
    font-weight: 400;
    font-size: 1.2rem;        /* fallback — overridden by intro.js */
    letter-spacing: 0.12em;   /* fallback — overridden by intro.js */
    text-transform: lowercase;
    line-height: 1;
    white-space: nowrap;
    color: #D8CA9D;
    text-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
    opacity: 0;
    will-change: transform, opacity, color;
}

html.intro-play .intro__title {
    animation: introFadeUp 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1.5s forwards;
}

/* 金丰寸 is the logo's own artwork, cropped out of it — not live text.
   The logo is set in a bespoke face we have no file for, so the old font
   stack fell through to whatever CJK font the OS happened to have
   (PingFang SC on macOS, Microsoft YaHei on Windows) and drew noticeably
   different glyph shapes. Sized so the block spans about the width the
   letter-spaced text did (~73px), which also lands on the logo's own
   proportion of characters to wordmark (125:225); the tracking within it
   is now the logo's rather than the 0.5em faked before. Fixed rather than
   tracking the title, exactly as the 1.15rem text was.
   The bitmap is 127px wide but paints at ~73px, so it stays crisp even on
   a 2x/3x display. */
.intro__chinese {
    display: block;
    height: 1.8rem;
    width: auto;
    opacity: 0;
    will-change: opacity;
}

html.intro-play .intro__chinese {
    animation: introFadeUp 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) 1.65s forwards;
}

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

/* While the intro runs: the hero content block is present (so the
   travelling title can measure the logo's real position) but its
   two parts are held back individually —
     • the wordmark is hidden until the glide lands on it;
     • the nav is hidden until the wordmark has arrived, then it
       gently fades in (see .hero__nav--enter below). */
html.intro-active .hero__content {
    opacity: 1;
    animation: none;
}

html.intro-active .hero__logo {
    visibility: hidden;
}

/* The corner flask is hidden until the intro flask has glided
   down onto it (revealed by intro.js at the seamless swap). */
html.intro-active .corner-flask {
    visibility: hidden;
}

html.intro-active #main-nav {
    opacity: 0;
}

/* Return visit in the same session (html.intro-seen): the intro is
   skipped entirely. The hero content — including the ALCHEMIST
   wordmark — is ever-present from the first paint; only the nav
   headings are held back, then gently fade in once main.js has
   rendered them (via .hero__nav--enter below). */
html.intro-seen .hero__content {
    opacity: 1;
    animation: none;
}

html.intro-seen #main-nav {
    opacity: 0;
}

/* Nav fade-in — triggered by main.js once the intro wordmark has
   settled into place (or immediately when there is no intro). It
   fades the whole nav container, so EVERY header inside it fades
   in together — including any sections added later via the admin
   panel, with no extra wiring needed. */
#main-nav.hero__nav--enter {
    animation: navEnter 1.1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
}

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

@media (prefers-reduced-motion: reduce) {
    #main-nav.hero__nav--enter {
        animation-duration: 0.4s;
    }
}

/* ---- Landscape phones (short viewport) ---- */
@media (max-height: 500px) and (orientation: landscape) {
    .hero__content {
        left: 20px;
        top: 12px;
        gap: 12px;
    }

    .hero__logo {
        font-size: 1.02rem;
    }

    .hero__nav {
        gap: 0;
    }

    .hero__nav-link {
        min-height: 32px;
        line-height: 32px;
        font-size: 0.9rem;
    }

    .dropdown__item {
        min-height: 30px;
        line-height: 30px;
        font-size: 0.84rem;
    }

    .dropdown--open {
        max-height: 200px;
    }

    .corner-flask {
        right: 20px;
        bottom: 12px;
        width: 26px;
    }

    .slide-caption {
        left: 20px;
        bottom: 12px;
        font-size: 0.9rem;
        max-width: 70vw;
    }
}
