/* ============================================================
   TEE TIME — CLUBHOUSE SHELL
   ------------------------------------------------------------
   The chrome every room shares: the nav, the room header, the
   door cards, buttons, and badges.

   Requires tokens.css. Load order is always:
       tokens.css  →  clubhouse.css  →  the room's own stylesheet

   A room's own stylesheet may override SEMANTIC tokens
   (--surface, --ink, --accent). It must not restyle .ch-* classes
   — that is how three design systems happened the first time.
   ============================================================ */

/* ── Reset, scoped so it can't fight a room's existing reset ──*/
.ch-scope *,
.ch-scope *::before,
.ch-scope *::after { box-sizing: border-box; }

/* ============================================================
   NAVIGATION — the corridor between rooms
   Present on every page, identical everywhere. This bar is the
   single strongest signal that these are rooms and not websites.
   ============================================================ */

.ch-nav {
    position: sticky;
    flex-wrap: wrap;
    top: 0;
    z-index: 900;
    display: flex;
    align-items: center;
    gap: var(--s-4);
    min-height: var(--nav-height);
    padding: var(--s-2) var(--s-5);
    background: color-mix(in srgb, var(--surface) 88%, transparent);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line);
    font-family: var(--font-body);
}

/* color-mix is well supported but not universal — this is the
   fallback, declared first so modern browsers override it. */
@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .ch-nav { background: var(--surface); }
}

.ch-nav-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-family: var(--font-display);
    font-size: var(--t-md);
    font-weight: 700;
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
    letter-spacing: -0.01em;
}

.ch-nav-brand:hover { color: var(--accent); }
.ch-nav-brand:hover .ch-nav-logo { opacity: 0.82; }
.ch-nav-logo { transition: opacity var(--dur-fast) var(--ease); }

.ch-nav-brand-mark {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--accent-ink);
    font-size: var(--t-sm);
}



.ch-member {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-1) var(--s-1) var(--s-1) var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface-raised);
    font-size: var(--t-sm);
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
}

.ch-member-name { font-weight: 600; }

.ch-member-avatar {
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    border-radius: var(--r-pill);
    background: var(--tt-espresso);
    color: var(--tt-cream);
    font-size: var(--t-xs);
    font-weight: 700;
    text-transform: uppercase;
}

.ch-tier {
    padding: 2px var(--s-2);
    border-radius: var(--r-pill);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.ch-tier-guest  { background: var(--line);      color: var(--ink-muted); }
.ch-tier-friend { background: var(--tt-gold);   color: var(--tt-espresso); }
.ch-tier-member { background: var(--accent);    color: var(--accent-ink); }
.ch-tier-admin  { background: var(--tt-espresso); color: var(--tt-cream); }

/* ============================================================
   PAGE FURNITURE
   ============================================================ */

/* The browser's default body margin is 8px, and nothing here was
   resetting it — so every clubhouse page rendered inside a thin white
   frame while the rooms (which reset it in their own CSS) went edge to
   edge. Scoped to body.ch-page so a room that wants its own margins
   keeps them. */
body.ch-page {
    margin: 0;
    padding: 0;
}

.ch-page {
    min-height: 100vh;
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-body);
    line-height: var(--lh-body);
    overflow-x: hidden;      /* nothing should ever scroll sideways */
}

.ch-container {
    width: 100%;
    max-width: var(--width-page);
    margin-inline: auto;
    padding-inline: var(--s-5);
}

.ch-section { padding-block: var(--s-8); }

.ch-room-header { padding-block: var(--s-8) var(--s-6); }

.ch-eyebrow {
    display: block;
    margin-bottom: var(--s-3);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--accent);
}

.ch-title {
    margin: 0 0 var(--s-3);
    font-family: var(--font-display);
    font-size: clamp(var(--t-xl), 5vw, var(--t-3xl));
    font-weight: 700;
    line-height: var(--lh-tight);
    letter-spacing: -0.02em;
}

.ch-lead {
    max-width: var(--width-prose);
    margin: 0;
    font-size: var(--t-md);
    color: var(--ink-muted);
}

.ch-heading {
    margin: 0 0 var(--s-4);
    font-family: var(--font-display);
    font-size: var(--t-lg);
    font-weight: 700;
    line-height: var(--lh-snug);
}

/* ============================================================
   DOOR CARDS — how a room advertises itself
   ============================================================ */

.ch-doors {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: var(--s-5);
}

.ch-door {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    padding: var(--s-6);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: var(--surface-raised);
    color: inherit;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    transition: transform var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                border-color var(--dur-base) var(--ease);
}

.ch-door:hover {
    transform: translateY(-4px);
    border-color: var(--accent);
    box-shadow: var(--shadow-lg);
}

.ch-door-icon {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    border-radius: var(--r-md);
    background: color-mix(in srgb, var(--accent) 14%, transparent);
    font-size: var(--t-lg);
}

@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .ch-door-icon { background: var(--surface-sunken); }
}

.ch-door-name {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--t-lg);
    font-weight: 700;
    line-height: var(--lh-snug);
}

.ch-door-tagline {
    margin: 0;
    font-size: var(--t-sm);
    color: var(--ink-muted);
}

.ch-door-blurb {
    margin: 0;
    font-size: var(--t-sm);
    color: var(--ink-muted);
    flex: 1;
}

.ch-door-foot {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    flex-wrap: wrap;
    margin-top: auto;
    padding-top: var(--s-3);
    border-top: 1px solid var(--line);
}

.ch-door.is-locked   { opacity: 0.72; }
.ch-door.is-planned  { opacity: 0.55; pointer-events: none; border-style: dashed; }

/* Sub-doors — the rooms within a room. */
.ch-door-links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ch-door-link {
    padding: var(--s-1) var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-pill);
    background: var(--surface);
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-muted);
    text-decoration: none;
}

.ch-door-link:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/* ============================================================
   BUTTONS & BADGES
   ============================================================ */

.ch-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s-2);
    padding: var(--s-3) var(--s-5);
    border: 1px solid transparent;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--accent-ink);
    font-family: var(--font-body);
    font-size: var(--t-sm);
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease),
                transform var(--dur-fast) var(--ease),
                box-shadow var(--dur-fast) var(--ease);
}

.ch-btn:hover {
    background: var(--accent-hover);
    transform: translateY(-1px);
    box-shadow: var(--shadow-accent);
}

.ch-btn:active { transform: translateY(0); }

.ch-btn-ghost {
    background: transparent;
    border-color: var(--line-strong);
    color: var(--ink);
}
.ch-btn-ghost:hover {
    background: transparent;
    border-color: var(--accent);
    color: var(--accent);
    box-shadow: none;
}

.ch-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
    padding: 3px var(--s-2);
    border-radius: var(--r-pill);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.ch-badge-open    { background: color-mix(in srgb, var(--tt-good) 16%, transparent); color: var(--tt-good); }
.ch-badge-building{ background: color-mix(in srgb, var(--tt-warn) 18%, transparent); color: var(--tt-warn); }
.ch-badge-planned { background: var(--line); color: var(--ink-muted); }
.ch-badge-locked  { background: var(--line); color: var(--ink-muted); }

@supports not (background: color-mix(in srgb, red 50%, blue)) {
    .ch-badge-open, .ch-badge-building { background: var(--surface-sunken); }
}

/* The live pulse. Reserved for genuinely-live data. */
.ch-live {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--tt-live);
}

.ch-live::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: var(--r-pill);
    background: var(--tt-live);
    animation: ch-pulse 1.8s var(--ease) infinite;
}

@keyframes ch-pulse {
    0%, 100% { opacity: 1;   transform: scale(1); }
    50%      { opacity: 0.4; transform: scale(0.8); }
}

/* ============================================================
   FOOTER
   ============================================================ */

.ch-foot {
    margin-top: var(--s-9);
    padding-block: var(--s-6);
    border-top: 1px solid var(--line);
    font-size: var(--t-sm);
    color: var(--ink-muted);
}

.ch-foot-inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-4);
    align-items: center;
    justify-content: space-between;
}

.ch-foot a { color: var(--ink-muted); text-decoration: underline; }
.ch-foot a:hover { color: var(--accent); }

/* ── Utility ─────────────────────────────────────────────────*/
.ch-visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ============================================================
   GET THE APP
   Shown on the Lobby only when store links are configured in
   CLUBHOUSE.SITE. Driving downloads is the web front door's job,
   so this sits with the primary actions rather than in a footer.
   ============================================================ */

.ch-get-app {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    margin-top: var(--s-6);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
    max-width: var(--width-prose);
}

.ch-get-app-label {
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink-muted);
}

.ch-get-app-links {
    display: flex;
    gap: var(--s-3);
    flex-wrap: wrap;
}

/* The hidden attribute has no weight against a display rule, so a
   component that sets `display` must opt back out explicitly. This
   bit the "get the app" row: it set display:flex, so hidden did
   nothing and the label rendered above an empty space. */
.ch-scope [hidden] { display: none !important; }

/* ============================================================
   THE ENTRANCE  —  index.html
   ------------------------------------------------------------
   The front door. One full-bleed frame, a slow montage behind it,
   and one thing to do: come in.

   The montage is stills cross-fading rather than video, and that is
   a deliberate trade rather than a placeholder. Five JPEGs total
   about the weight of two seconds of 1080p, they start painting on
   the first frame instead of after a buffer, they survive a slow
   phone connection, and iOS never refuses to autoplay them. If real
   footage arrives it drops in as assets/hero.mp4 and plays on top —
   the montage becomes the fallback, and nothing else changes.
   ============================================================ */

.ch-entrance {
    position: relative;
    min-height: 100svh;          /* svh, not vh — vh includes mobile browser
                                    chrome, so a hero sized in vh gets its
                                    bottom cropped on iPhone */
    display: grid;
    place-items: center;
    overflow: hidden;
    background: var(--tt-espresso);
    text-align: center;
}

.ch-stage { position: absolute; inset: 0; }

.ch-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    /* Real photographs, not the drawn placeholders these overlays were
       first tuned against. A Maine bluebird sky is far brighter than a
       rendered dusk, and cream text on it is unreadable. Darkening the
       source here rather than piling on more scrim keeps one control
       for it — turn this up and the whole montage lifts. */
    filter: brightness(0.72) saturate(0.95);
    /* A very slow drift. Enough that the frame is never quite still,
       far too slow to be read as an animation. */
    transform: scale(1.06);
    transition: opacity 1.6s var(--ease), transform 9s linear;
    will-change: opacity, transform;
}

.ch-slide.is-live {
    opacity: 1;
    transform: scale(1.14);
}

.ch-entrance video.ch-slide { opacity: 0; }
.ch-entrance video.ch-slide.is-live { opacity: 1; transform: none; }

/* Two overlays doing different jobs. The scrim lifts text contrast
   off whatever the photograph is doing; the wash keeps the frame
   inside the brand instead of letting raw image colour take over. */
.ch-entrance-scrim {
    position: absolute;
    inset: 0;
    background:
        radial-gradient(ellipse 72% 60% at 50% 47%,
            rgba(18, 8, 0, 0.52) 0%,
            rgba(18, 8, 0, 0.30) 48%,
            rgba(18, 8, 0, 0.46) 100%);
}

.ch-entrance-wash {
    position: absolute;
    inset: 0;
    background: linear-gradient(180deg,
        rgba(31, 14, 0, 0.72) 0%,
        rgba(31, 14, 0, 0.10) 22%,
        rgba(31, 14, 0, 0.14) 55%,
        rgba(31, 14, 0, 0.44) 82%,
        rgba(31, 14, 0, 0.80) 100%);
}

.ch-entrance-inner {
    position: relative;
    width: 100%;
    max-width: 48rem;
    padding: var(--s-9) var(--s-5);
}

/* The full lockup, sized so the hand-lettering is legible rather than a
   scribble. Width-driven and capped, because the mark is wider than it
   is tall — a square box was the reason it kept coming out tiny. */
.ch-entrance-mark {
    width: min(340px, 62vw);
    height: auto;
    margin: 0 auto var(--s-4);
    display: block;
    object-fit: contain;
    filter: drop-shadow(0 8px 30px rgba(0, 0, 0, 0.6));
}

/* Stands in for the logo until there is a logo file to stand in for.
   assets/teetimelogo.png is a 2-byte placeholder, so the <img> is
   hidden until it decodes and this rule carries the entrance. */
.ch-entrance-rule {
    width: 64px;
    height: 3px;
    margin: 0 auto var(--s-5);
    border-radius: var(--r-pill);
    background: var(--tt-orange);
    box-shadow: 0 0 24px rgba(206, 100, 40, 0.6);
}

.ch-entrance h1 {
    margin: 0 0 var(--s-3);
    font-family: var(--font-display);
    font-size: clamp(3rem, 8.5vw, 6rem);
    font-weight: 900;
    line-height: 0.98;
    letter-spacing: -0.03em;
    color: var(--tt-cream);
    text-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
}

.ch-entrance-sub {
    margin: 0 0 var(--s-5);
    font-family: var(--font-display);
    font-size: clamp(1.25rem, 3.4vw, 2rem);
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--tt-orange-warm);
}

.ch-entrance-lead {
    max-width: 34rem;
    margin: 0 auto var(--s-6);
    font-size: var(--t-md);
    line-height: var(--lh-body);
    color: rgba(245, 230, 211, 0.86);
    text-shadow: 0 2px 16px rgba(0, 0, 0, 0.5);
}

.ch-entrance-actions {
    display: flex;
    gap: var(--s-3);
    flex-wrap: wrap;
    justify-content: center;
}

/* The entrance is a mud room now: sign in, or look around first.
   Two buttons need to read as one primary and one alternative, not
   as a pair of equals — a guest who cannot tell which is the way in
   has been given a decision instead of a door. */
.ch-entrance-note {
    margin: var(--sp-3) auto 0;
    max-width: 34rem;
    font-size: var(--t-sm);
    line-height: 1.5;
    color: var(--text-muted, rgba(255,255,255,0.72));
    text-align: center;
}

.ch-entrance-actions .ch-btn-ghost {
    /* Secondary, and legible on the entrance photograph — the page
       default assumes a light surface and vanishes here. */
    color: #fff;
    border-color: rgba(255,255,255,0.55);
    background: rgba(0,0,0,0.18);
}
.ch-entrance-actions .ch-btn-ghost:hover {
    background: rgba(0,0,0,0.32);
    border-color: rgba(255,255,255,0.8);
}

/* The one thing to do. Bigger than a normal button on purpose —
   on this page there is no competing action. */
.ch-btn-door {
    padding: var(--s-4) var(--s-6);
    font-size: var(--t-md);
    box-shadow: 0 10px 40px rgba(206, 100, 40, 0.42);
}
.ch-btn-door:hover { box-shadow: 0 12px 48px rgba(206, 100, 40, 0.55); }

/* On the hero the ghost button sits on a photograph, so it needs its
   own treatment — the daylight version would vanish. */
.ch-entrance-actions .ch-btn-ghost,
.ch-presence .ch-btn-ghost {
    border-color: rgba(245, 230, 211, 0.45);
    color: var(--tt-cream);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.ch-entrance-actions .ch-btn-ghost:hover,
.ch-presence .ch-btn-ghost:hover {
    border-color: var(--tt-cream);
    color: var(--tt-cream);
    background: rgba(245, 230, 211, 0.12);
}

/* Slide dots — also the manual control, so they are buttons. */
.ch-slide-dots {
    position: absolute;
    left: 50%;
    bottom: var(--s-6);
    transform: translateX(-50%);
    display: flex;
    gap: var(--s-2);
    padding: 0;
    margin: 0;
    list-style: none;
    z-index: 2;
}

.ch-slide-dot {
    width: 30px;
    height: 3px;
    padding: 0;
    border: 0;
    border-radius: var(--r-pill);
    background: rgba(245, 230, 211, 0.30);
    cursor: pointer;
    transition: background var(--dur-base) var(--ease);
}
.ch-slide-dot[aria-current="true"] { background: var(--tt-orange); }
.ch-slide-dot:hover { background: rgba(245, 230, 211, 0.62); }

/* ============================================================
   THE ENTRANCE — nav over the photograph
   ------------------------------------------------------------
   The nav stays sticky and in flow; the hero is pulled up under it
   by exactly the nav's height. Doing it that way means the only
   thing that changes on scroll is COLOUR — nothing is repositioned,
   so there is no layout shift at the boundary.
   ============================================================ */

.ch-page-lobby .ch-entrance { margin-top: calc(var(--nav-height) * -1); }

.ch-page-lobby .ch-nav {
    transition: background var(--dur-base) var(--ease),
                border-color var(--dur-base) var(--ease);
}

.ch-page-lobby[data-nav-mode="over"] .ch-nav {
    background: transparent;
    border-bottom-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
}

.ch-page-lobby[data-nav-mode="over"] .ch-nav-brand,
.ch-page-lobby[data-nav-mode="over"] .ch-member-name {
    color: var(--tt-cream);
}

.ch-page-lobby[data-nav-mode="over"] .ch-member {
    border-color: rgba(245, 230, 211, 0.30);
    background: rgba(18, 8, 0, 0.28);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}

.ch-page-lobby[data-nav-mode="over"] .ch-tier {
    background: rgba(245, 230, 211, 0.16);
    color: var(--tt-cream);
}

.ch-page-lobby[data-nav-mode="over"] .ch-member-avatar {
    background: rgba(245, 230, 211, 0.16);
    color: var(--tt-cream);
}

/* ============================================================
   THE FLOOR  —  clubhouse.html
   ------------------------------------------------------------
   The room grid. It used to be edge to edge with no gutters, so
   that the rooms read as one building rather than a row of
   separate products.

   They now stand apart, in rows of three, and the reasoning
   reversed for a reason worth keeping: the rooms ARE separate
   products — Tee Time Live, Next On The Tee and the Locker Room
   are three destinations, not three panels of one screen — and
   eight tiles fused at the edges read as a contact sheet, where
   nothing is more important than anything else.

   What holds the building together is the corridor, the palette
   and the duotone wash on every tile. That work is done in the
   shell, which means the tiles are free to be cards.
   ============================================================ */

.ch-floor-head {
    padding: var(--s-8) var(--s-5) var(--s-7);
    text-align: center;
    background: var(--surface-sunken);
    border-bottom: 1px solid var(--line);
}

.ch-floor-head h1 {
    margin: var(--s-2) 0 var(--s-3);
    font-family: var(--font-display);
    font-size: clamp(2.2rem, 5vw, 3.4rem);
    font-weight: 900;
    letter-spacing: -0.025em;
    color: var(--ink);
}

.ch-floor-head p {
    max-width: 40rem;
    margin: 0 auto;
    color: var(--ink-muted);
    line-height: var(--lh-body);
}

/* The floor holds one grid per row, so the space between rows is
   larger than the space between tiles in a row. That difference is
   what makes "the three that are open" read as a group. */
.ch-floor-rooms {
    max-width: 1180px;
    margin: 0 auto;
    padding: var(--s-7) var(--s-5) var(--s-8);
    display: grid;
    gap: var(--s-7);
}

.ch-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s-5);
}

/* A row of one — the staff row, when an admin is looking — must not
   stretch a single tile across the full width. */
.ch-tiles-staff {
    grid-template-columns: repeat(3, 1fr);
}
.ch-tiles-staff > .ch-tile:only-child {
    grid-column: span 1;
}

/* ── The feature row ──────────────────────────────────────
   Tee times are the whole concept of the app — sharing one,
   finding one, filling a group — so on the floor they get a row
   to themselves rather than a seventh slot in a grid.

   Wide and centred, not tall: a landscape tile at the top of a
   page of portrait ones reads as a headline rather than as the
   biggest item in a list. It also stops the fold falling in the
   middle of the first row of rooms. */
.ch-tiles-feature {
    grid-template-columns: 1fr;
    justify-items: center;
}

.ch-tiles-feature > .ch-tile {
    width: 100%;
    max-width: 780px;
    aspect-ratio: 21 / 9;
}

/* --t-2xl, not --t-xxl. The first version wrote a token that does
   not exist, CSS dropped the whole declaration, and the headline
   tile silently rendered at the same size as the rooms below it.
   An invalid custom property fails quietly — worth remembering. */
.ch-tiles-feature .ch-tile-name  { font-size: var(--t-2xl); }
.ch-tiles-feature .ch-tile-icon  { font-size: 2.6rem; margin-bottom: var(--s-2); }
.ch-tiles-feature .ch-tile-brand { font-size: var(--t-sm); letter-spacing: 0.18em; }

/* The reveal is collapsed until hover on a room tile, which is right
   for a grid of nine. This is one tile and it is the headline — its
   tagline should be readable without asking. */
.ch-tiles-feature .ch-tile-reveal { grid-template-rows: 1fr; opacity: 1; }
.ch-tiles-feature .ch-tile-tagline { font-size: var(--t-md); }

/* The wash is tuned for a photograph in a portrait tile. Over the
   gradient in a wide one it only muddies it, so it steps back. */
.ch-tiles-feature > .ch-tile::before {
    background:
        radial-gradient(ellipse at center,
            rgba(18, 8, 0, 0.05) 0%,
            rgba(18, 8, 0, 0.28) 65%,
            rgba(18, 8, 0, 0.55) 100%);
}

/* ── The tile's own surface ───────────────────────────────
   THE BRAND GRADIENT, ON EVERY TILE.

   The eight room tiles were drawn placeholders — SVG scenes
   screenshotted by tools/render-scenes.js. Next to the Tee Times
   tile, which has no picture and takes the gradient, they read as
   a different product again: the exact problem this whole effort
   exists to undo. One treatment until there is real photography.

   The hot-spot rotates by position (below) so six cards in a row
   are a family rather than a photocopy. Same gradient, different
   light — "similar", which is what was asked for, and what stops
   a wall of identical cards.

   A photograph, when one exists, is an <img> laid over this by
   clubhouse.html — so this is also the backstop if one ever fails
   to load. Flip SITE.tilePhotos in rooms.js to turn them on. */
.ch-tile {
    position: relative;
    display: block;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    border-radius: var(--r-md);
    text-decoration: none;
    isolation: isolate;
    background:
        radial-gradient(ellipse at 78% 22%,
            var(--tt-orange) 0%, transparent 58%),
        linear-gradient(120deg,
            var(--tt-espresso) 0%,
            var(--tt-espresso-2) 42%,
            var(--tt-orange-deep) 100%);
    box-shadow: var(--shadow-md);
    /* Only transform and box-shadow animate here — the photograph
       inside has its own transition, and compositing the card and
       its contents separately keeps the lift smooth. */
    transition: transform var(--dur-base) var(--ease),
                box-shadow var(--dur-base) var(--ease);
}

/* Standing apart means hover has to do more work: with no shared
   edges, a tile that only brightens does not clearly separate from
   its neighbours. It lifts. */
.ch-tile:hover,
.ch-tile:focus-visible {
    transform: translateY(-6px);
    box-shadow: var(--shadow-lg);
}

.ch-tile:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.ch-tile img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transform: scale(1.02);
    transition: transform var(--dur-slow) var(--ease),
                filter var(--dur-slow) var(--ease);
    filter: saturate(0.92) brightness(0.88);
}

/* Duotone wash. Every tile is a different photograph; without this
   the grid reads as a stock-image collage instead of one place.
   Gradient is radial so centered text has even legibility across
   the whole tile, not just the bottom edge. */
.ch-tile::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background:
        radial-gradient(ellipse at center,
            rgba(18, 8, 0, 0.18) 0%,
            rgba(18, 8, 0, 0.54) 60%,
            rgba(18, 8, 0, 0.82) 100%);
    transition: background var(--dur-base) var(--ease);
}

.ch-tile-body {
    position: absolute;
    inset: 0;               /* covers the whole tile */
    z-index: 2;
    padding: var(--s-5);
    color: var(--tt-cream);
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.ch-tile-icon {
    display: block;
    font-size: 2rem;
    margin-bottom: var(--s-3);
    filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.7));
}

.ch-tile-name {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--t-xl);     /* larger: was t-lg */
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.01em;
    text-shadow: 0 2px 14px rgba(0, 0, 0, 0.65);
}

.ch-tile-brand {
    display: block;
    font-family: var(--font-body);
    font-size: var(--t-xs);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--tt-orange-warm);
    margin-bottom: var(--s-2);
}

/* The reveal. Collapsed to nothing until hover/focus, so the resting
   grid stays quiet and the hovered tile is unambiguous. */
.ch-tile-reveal {
    display: grid;
    grid-template-rows: 0fr;
    opacity: 0;
    transition: grid-template-rows var(--dur-slow) var(--ease),
                opacity var(--dur-base) var(--ease);
}
.ch-tile-reveal > div { overflow: hidden; }

.ch-tile:hover  .ch-tile-reveal,
.ch-tile:focus-visible .ch-tile-reveal,
.ch-tile.is-open .ch-tile-reveal {
    grid-template-rows: 1fr;
    opacity: 1;
}

.ch-tile:hover img,
.ch-tile:focus-visible img {
    transform: scale(1.10);
    filter: saturate(1) brightness(0.86);
}

.ch-tile:hover::before,
.ch-tile:focus-visible::before {
    background:
        radial-gradient(ellipse at center,
            rgba(18, 8, 0, 0.10) 0%,
            rgba(18, 8, 0, 0.44) 55%,
            rgba(18, 8, 0, 0.78) 100%);
}

.ch-tile-tagline {
    margin: var(--s-2) 0 0;
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: rgba(245, 230, 211, 0.82);
}

.ch-tile-go {
    display: inline-block;
    margin-top: var(--s-3);
    font-size: var(--t-sm);
    font-weight: 700;
    color: var(--tt-orange-warm);
}

.ch-tile-flag {
    position: absolute;
    top: var(--s-4);
    right: var(--s-4);
    z-index: 2;
}

/* ── Same gradient, different light ───────────────────────
   Six identical cards in a grid look like a rendering fault. The
   hot-spot walks around the tile by position, which keeps them
   obviously one family while giving the floor some movement.
   Position, not room id, so adding or reordering rooms needs no
   thought here. */
.ch-tiles > .ch-tile:nth-child(3n+2) {
    background:
        radial-gradient(ellipse at 20% 78%,
            var(--tt-orange) 0%, transparent 58%),
        linear-gradient(150deg,
            var(--tt-espresso) 0%,
            var(--tt-espresso-2) 46%,
            var(--tt-orange-deep) 100%);
}
.ch-tiles > .ch-tile:nth-child(3n+3) {
    background:
        radial-gradient(ellipse at 50% 8%,
            var(--tt-orange) 0%, transparent 62%),
        linear-gradient(200deg,
            var(--tt-espresso) 0%,
            var(--tt-espresso-2) 38%,
            var(--tt-orange-deep) 100%);
}

/* A room you cannot walk into yet. It still shows — knowing the
   clubhouse is bigger than the part that is finished is the point —
   but it does not pretend to be a door.

   The gradient goes cold rather than orange. With no photograph to
   desaturate, that is what carries "closed": the whole floor is
   warm, and these are not. */
.ch-tile.is-shut { cursor: default; }

.ch-tiles > .ch-tile.is-shut,
.ch-tiles > .ch-tile.is-shut:nth-child(3n+2),
.ch-tiles > .ch-tile.is-shut:nth-child(3n+3) {
    background:
        radial-gradient(ellipse at 60% 15%,
            var(--n-400) 0%, transparent 62%),
        linear-gradient(150deg,
            var(--tt-espresso) 0%,
            var(--n-600) 55%,
            var(--n-500) 100%);
}

.ch-tile.is-shut img { filter: saturate(0.22) brightness(0.55); }
.ch-tile.is-shut:hover img { transform: scale(1.02); filter: saturate(0.30) brightness(0.62); }

@media (max-width: 900px) {
    .ch-tiles,
    .ch-tiles-staff { grid-template-columns: repeat(2, 1fr); }
    .ch-tiles-feature > .ch-tile { aspect-ratio: 16 / 9; }
    .ch-floor-rooms { padding: var(--s-6) var(--s-4) var(--s-7); gap: var(--s-6); }
}

@media (max-width: 560px)  {
    .ch-tiles,
    .ch-tiles-staff { grid-template-columns: 1fr; }
    .ch-tile  { aspect-ratio: 4 / 3; }
    .ch-tiles-feature > .ch-tile { aspect-ratio: 4 / 3; }
    .ch-tiles-feature .ch-tile-name { font-size: var(--t-xl); }
    .ch-tiles-feature .ch-tile-tagline { font-size: var(--t-sm); }
    /* Nothing hovers on a phone, so the reveal is always open — and
       the lift is dead weight on a touch screen. */
    .ch-tile-reveal { grid-template-rows: 1fr; opacity: 1; }
    .ch-tile:hover, .ch-tile:focus-visible { transform: none; }
}

/* ============================================================
   THE SOCIAL FLOOR
   ------------------------------------------------------------
   Slack's shape, because it is the shape people already know: a
   list of rooms on the left, the conversation on the right.

   NOTHING IN HERE INVENTS A PERSON OR A MESSAGE. There is no web
   session yet, so the clubhouse genuinely cannot see who is inside.
   The conversation panel is labelled a preview, in the markup and
   on screen, and the group counts are absent rather than guessed.
   Filling this with plausible names would break the one promise the
   clubhouse is built on, on first contact.
   ============================================================ */

.ch-social {
    background: var(--tt-espresso);
    color: var(--tt-cream);
    padding-block: var(--s-8);
}

.ch-social .ch-heading { color: var(--tt-cream); }

.ch-social-lede {
    max-width: 42rem;
    margin: var(--s-2) 0 var(--s-6);
    color: rgba(245, 230, 211, 0.72);
    line-height: var(--lh-body);
}

/* ── presence strip ──────────────────────────────────────────*/
.ch-presence {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    flex-wrap: wrap;
    margin-bottom: var(--s-6);
    padding: var(--s-4) var(--s-5);
    border: 1px solid rgba(245, 230, 211, 0.14);
    border-radius: var(--r-md);
    background: rgba(245, 230, 211, 0.04);
}

.ch-presence-dot {
    width: 10px;
    height: 10px;
    flex: none;
    border-radius: var(--r-pill);
    background: var(--tt-orange);
    box-shadow: 0 0 0 4px rgba(206, 100, 40, 0.22);
}

.ch-presence-who {
    font-family: var(--font-display);
    font-size: var(--t-md);
    font-weight: 700;
}

.ch-presence-note {
    flex: 1 1 18rem;
    font-size: var(--t-sm);
    color: rgba(245, 230, 211, 0.62);
}

/* ── the board ───────────────────────────────────────────────*/
.ch-board {
    display: grid;
    grid-template-columns: minmax(230px, 300px) 1fr;
    gap: 1px;
    background: rgba(245, 230, 211, 0.14);
    border: 1px solid rgba(245, 230, 211, 0.14);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.ch-board-side,
.ch-board-main { background: #170A00; }

.ch-board-side { padding: var(--s-4) 0; }

.ch-board-label {
    padding: 0 var(--s-4) var(--s-3);
    font-size: var(--t-xs);
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: rgba(245, 230, 211, 0.42);
}

.ch-channel {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    width: 100%;
    padding: var(--s-3) var(--s-4);
    border: 0;
    background: none;
    color: rgba(245, 230, 211, 0.80);
    font: inherit;
    font-size: var(--t-sm);
    text-align: left;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease);
}

.ch-channel:hover { background: rgba(245, 230, 211, 0.06); color: var(--tt-cream); }

.ch-channel[aria-current="true"] {
    background: rgba(206, 100, 40, 0.20);
    color: var(--tt-cream);
    box-shadow: inset 3px 0 0 var(--tt-orange);
}

.ch-channel-icon { flex: none; width: 1.4em; text-align: center; }
.ch-channel-name { flex: 1; }

.ch-channel-state {
    font-size: var(--t-xs);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: rgba(245, 230, 211, 0.38);
}

.ch-board-main {
    display: flex;
    flex-direction: column;
    min-height: 26rem;
}

.ch-board-head {
    display: flex;
    align-items: baseline;
    gap: var(--s-3);
    flex-wrap: wrap;
    padding: var(--s-4) var(--s-5);
    border-bottom: 1px solid rgba(245, 230, 211, 0.12);
}

.ch-board-title {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--t-md);
    font-weight: 700;
}

.ch-board-sub {
    font-size: var(--t-sm);
    color: rgba(245, 230, 211, 0.55);
}

/* The preview band. Loud on purpose — it has to be impossible to
   mistake the sample thread below it for live activity. */
.ch-preview-band {
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-5);
    background: rgba(206, 100, 40, 0.16);
    border-bottom: 1px solid rgba(206, 100, 40, 0.35);
    font-size: var(--t-xs);
    letter-spacing: 0.06em;
    color: var(--tt-orange-warm);
}

.ch-thread {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
    padding: var(--s-5);
}

.ch-msg { display: flex; gap: var(--s-3); }

.ch-msg-avatar {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    flex: none;
    border-radius: var(--r-sm);
    background: rgba(245, 230, 211, 0.10);
    font-size: var(--t-sm);
}

.ch-msg-who {
    font-weight: 700;
    font-size: var(--t-sm);
    color: var(--tt-cream);
}

.ch-msg-when {
    margin-left: var(--s-2);
    font-size: var(--t-xs);
    color: rgba(245, 230, 211, 0.38);
}

.ch-msg-text {
    margin: 2px 0 0;
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: rgba(245, 230, 211, 0.78);
}

.ch-board-foot {
    padding: var(--s-4) var(--s-5);
    border-top: 1px solid rgba(245, 230, 211, 0.12);
    font-size: var(--t-sm);
    color: rgba(245, 230, 211, 0.55);
}

.ch-board-empty {
    flex: 1;
    display: grid;
    place-items: center;
    padding: var(--s-7) var(--s-5);
    text-align: center;
}

.ch-board-empty p {
    max-width: 30rem;
    margin: var(--s-3) auto 0;
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: rgba(245, 230, 211, 0.60);
}

@media (max-width: 780px) {
    .ch-board { grid-template-columns: 1fr; }
    .ch-board-side {
        display: flex;
        gap: var(--s-1);
        overflow-x: auto;
        padding: var(--s-3);
        scrollbar-width: none;
    }
    .ch-board-side::-webkit-scrollbar { display: none; }
    .ch-board-label { display: none; }
    .ch-channel {
        width: auto;
        white-space: nowrap;
        border-radius: var(--r-pill);
        padding: var(--s-2) var(--s-3);
    }
    .ch-channel[aria-current="true"] { box-shadow: none; }
    .ch-channel-state { display: none; }
}

/* ============================================================
   Motion off
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
    .ch-tile:hover, .ch-tile:focus-visible { transform: none; }
    .ch-slide,
    .ch-tile img,
    .ch-tile-reveal { transition-duration: 0.01ms; }
    .ch-slide.is-live { transform: none; }
}

/* ============================================================
   THE CORRIDOR, REBUILT AROUND IDENTITY
   ------------------------------------------------------------
   The bar used to list rooms. It doesn't any more, and that is the
   point: rooms are reached from the floor, which shows all of them
   with their real state. A short list in the bar could only ever
   show a few, and it silently truncated past about six.

   What belongs up here is who you are and what you can change about
   yourself — the two things that follow you between rooms.

       [ logo  Tee Time ]  [ Profile ]  · room you are in ·   [ you ]

   The brand is the way back to the floor from anywhere, which is
   what keeps the two-click rule.
   ============================================================ */

/* The whole lockup, not a cropped circle. It is a wide mark, so it is
   sized by HEIGHT and allowed to take whatever width that needs —
   object-fit: cover on a fixed square was what cropped the words off. */
.ch-nav-logo {
    height: 56px;
    width: auto;
    display: block;
    object-fit: contain;
}

/* Shown only until the logo file decodes; see shell.js. */
.ch-nav-word { font: inherit; }

/* Where you are. Not a link — the brand already goes somewhere. */
.ch-nav-here {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    margin-left: var(--s-3);
    padding-left: var(--s-3);
    border-left: 1px solid var(--line);
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink-muted);
    white-space: nowrap;
}

.ch-nav-spacer { flex: 1; }

.ch-nav-action {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-3);
    border-radius: var(--r-pill);
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink-muted);
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease);
}
.ch-nav-action:hover {
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--ink);
}
.ch-nav-action[aria-current="page"] {
    background: var(--accent);
    color: var(--accent-ink);
}

/* Settings sits in the right-hand corner beside your name, so it
   gets a gear as well as the word. Below 640px the word goes and the
   gear stays — the corner is the tightest space in the bar, and an
   icon there is understood without it. */
.ch-nav-settings-icon { font-size: 1.05em; line-height: 1; }

.ch-member[aria-current="page"] {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent);
}

/* Signed out. This is the only place the clubhouse asks for anything,
   so it gets the accent rather than blending in. */
.ch-signin {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-4);
    border: 0;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--accent-ink);
    font: inherit;
    font-size: var(--t-sm);
    font-weight: 700;
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--dur-fast) var(--ease);
}
.ch-signin:hover { background: var(--accent-hover); }

.ch-page-lobby[data-nav-mode="over"] .ch-nav-here {
    color: rgba(245, 230, 211, 0.72);
    border-left-color: rgba(245, 230, 211, 0.22);
}
.ch-page-lobby[data-nav-mode="over"] .ch-nav-action {
    color: rgba(245, 230, 211, 0.78);
}
.ch-page-lobby[data-nav-mode="over"] .ch-nav-action:hover {
    color: var(--tt-cream);
    background: rgba(245, 230, 211, 0.12);
}

@media (max-width: 640px) {
    .ch-nav-here { display: none; }   /* the page title says it already */
    .ch-nav-settings-word { display: none; }
    .ch-nav-settings { padding: var(--s-2); }
    /* The tier chip is the first thing to go — it repeats what the
       Sign in state already tells you, and the name must survive. */
    .ch-member .ch-tier { display: none; }
}

/* ============================================================
   SIGN-IN PANEL
   ------------------------------------------------------------
   Deliberately honest about what it is. There is no server-side
   account yet, so this remembers you ON THIS DEVICE and says so.
   When Firebase Auth lands, this panel is where it plugs in and the
   copy changes — the shape does not.
   ============================================================ */

/* ── A PANEL TALLER THAN THE SCREEN MUST STILL BE USABLE ──
   `place-items: center` centres the card and, when the card is
   taller than the viewport, centres it OFF BOTH ENDS — the heading
   above the top edge and the submit button below the bottom, with
   nothing to scroll because the overlay is fixed.

   It went unnoticed while the join panel was short. Adding one
   field pushed it over on a 720px-tall screen, which is an
   ordinary laptop, and the failure mode is the worst kind: the
   form looks fine and cannot be completed.

   `align-items: start` with `overflow: auto` lets a tall card
   scroll inside the overlay and leaves a short one where it was.
   The margin keeps it off the edges once it starts scrolling. */
.ch-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: grid;
    place-items: center;
    align-items: start;
    justify-items: center;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--s-5);
    background: rgba(18, 8, 0, 0.62);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}
.ch-modal[hidden] { display: none; }

.ch-modal-card {
    width: 100%;
    max-width: 26rem;
    /* Centred when it fits, top-aligned with room to scroll when it
       does not. auto margins do both without a media query. */
    margin: auto;
    padding: var(--s-6);
    border-radius: var(--r-lg);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-lg);
}

.ch-modal-card h2 {
    margin: 0 0 var(--s-2);
    font-family: var(--font-display);
    font-size: var(--t-lg);
    font-weight: 700;
}

.ch-modal-note {
    margin: 0 0 var(--s-5);
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: var(--ink-muted);
}

.ch-field { margin-bottom: var(--s-4); }

.ch-field label {
    display: block;
    margin-bottom: var(--s-2);
    font-size: var(--t-sm);
    font-weight: 600;
}

.ch-field input,
.ch-field select,
.ch-field textarea {
    width: 100%;
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface-raised);
    color: var(--ink);
    font: inherit;
    font-size: var(--t-base);
}
.ch-field input:focus,
.ch-field select:focus,
.ch-field textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    border-color: var(--accent);
}

.ch-field-hint {
    margin: var(--s-2) 0 0;
    font-size: var(--t-xs);
    color: var(--ink-faint);
}

.ch-modal-actions {
    display: flex;
    gap: var(--s-3);
    justify-content: flex-end;
    margin-top: var(--s-5);
}

/* ============================================================
   PROFILE
   ------------------------------------------------------------
   Cards in a responsive grid rather than one long column, because
   the four groupings are independent — you come here to change one
   thing, not to fill in a form top to bottom.
   ============================================================ */

.ch-note-light {
    display: block;
    margin: 0 0 var(--s-6);
    padding: var(--s-4) var(--s-5);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    background: var(--surface-sunken);
    color: var(--ink-muted);
    font-size: var(--t-sm);
    line-height: var(--lh-body);
}

.ch-profile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: var(--s-5);
    align-items: start;
}

.ch-card {
    padding: var(--s-5);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-raised);
}

.ch-card-title {
    margin: 0 0 var(--s-4);
    font-family: var(--font-display);
    font-size: var(--t-md);
    font-weight: 700;
    color: var(--ink);
}

.ch-card-note {
    margin: 0 0 var(--s-4);
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: var(--ink-muted);
}
.ch-card-note:last-child { margin: var(--s-4) 0 0; }

/* The save bar sits at the end of the form, not stuck to the viewport.
   A sticky bottom bar needs an opaque background to be readable, and
   that background paints over whatever is behind it — on a card grid
   this clipped the two tallest cards mid-field. With four short cards
   the form is never long enough to justify it. */
.ch-form-bar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--s-3);
    flex-wrap: wrap;
    margin-top: var(--s-6);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
}

.ch-form-status {
    margin-right: auto;
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--tt-good);
    opacity: 0;
    transition: opacity var(--dur-base) var(--ease);
}
.ch-form-status.is-shown { opacity: 1; }

/* ── Milestones ──────────────────────────────────────────────*/
.ch-milestones {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--s-2);
}

.ch-milestone {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding: var(--s-3);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--surface);
}

.ch-milestone-icon { font-size: var(--t-md); filter: grayscale(1); opacity: 0.55; }
.ch-milestone-name { flex: 1; font-size: var(--t-sm); font-weight: 600; }

.ch-milestone-state {
    font-size: var(--t-xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

/* settings.html — whether this browser knows you. A statement of
   fact, so it is ink rather than the green .ch-form-status uses to
   say "Saved." */
.ch-device-state {
    margin: var(--s-4) 0 var(--s-3);
    font-weight: 600;
    color: var(--ink);
}

/* ── Staff access ─────────────────────────────────────────
   Quiet on purpose. It sits below the settings cards, closed,
   and looks like a footnote rather than a door — because for
   everyone except the owner that is exactly what it is. */
.ch-staff {
    max-width: 34rem;
    margin: var(--s-7) auto 0;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-sunken);
}

.ch-staff > summary {
    padding: var(--s-4) var(--s-5);
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink-muted);
    cursor: pointer;
    list-style: none;
}
.ch-staff > summary::-webkit-details-marker { display: none; }

.ch-staff > summary::before {
    content: '🔐';
    margin-right: var(--s-2);
    filter: grayscale(1);
    opacity: 0.6;
}

.ch-staff > summary:hover { color: var(--ink); }
.ch-staff[open] > summary { color: var(--ink); }

.ch-staff-body {
    padding: 0 var(--s-5) var(--s-5);
    border-top: 1px solid var(--line);
    padding-top: var(--s-4);
}

/* The error under the passphrase field is a refusal, not a save, so
   it must not inherit the green .ch-form-status uses for "Saved." */
.ch-staff .ch-form-status.is-shown { color: var(--tt-bad); }

/* ============================================================
   TEE TIMES  —  tee-times.html
   ------------------------------------------------------------
   Mirrors the app's Tee Times and Club House tabs: the same three
   segments, the same search-by-course, the same card, the same
   AddTeeTime fields in the same order.

   NOT the same layout. The app is a phone: one column, a tab bar,
   a separate screen behind a ➕. This is a desktop page, so the
   feed and the form sit side by side. "Mirror" was asked for as
   visual style, not as screens — a web page that pretends to be a
   phone is worse on both counts.
   ============================================================ */

.ch-tt-layout {
    display: grid;
    grid-template-columns: 1.35fr 1fr;
    gap: var(--s-7);
    align-items: start;
}

/* ── The feed ─────────────────────────────────────────────── */

.ch-tt-feedhead {
    display: grid;
    gap: var(--s-4);
    margin-bottom: var(--s-5);
}

.ch-tt-feedhead .ch-heading { margin: 0; }
.ch-tt-search { margin: 0; }

/* Segmented control — iOS's shape, because that is what the app
   uses and what a member coming from it recognises. */
.ch-segments {
    display: inline-flex;
    gap: 2px;
    padding: 3px;
    border-radius: var(--r-sm);
    background: var(--surface-sunken);
}

.ch-segment {
    flex: 1;
    padding: var(--s-2) var(--s-4);
    border: 0;
    border-radius: calc(var(--r-sm) - 3px);
    background: transparent;
    font: inherit;
    font-size: var(--t-sm);
    font-weight: 600;
    color: var(--ink-muted);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--dur-fast) var(--ease),
                color var(--dur-fast) var(--ease);
}

.ch-segment:hover { color: var(--ink); }

.ch-segment[aria-selected="true"] {
    background: var(--surface-raised);
    color: var(--ink);
    box-shadow: var(--shadow-sm);
}

/* ── A tee time ───────────────────────────────────────────── */

.ch-tt-card {
    padding: var(--s-5);
    margin-bottom: var(--s-4);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-raised);
    box-shadow: var(--shadow-sm);
}

.ch-tt-cardhead {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    padding-bottom: var(--s-4);
    border-bottom: 1px solid var(--line);
}

.ch-tt-avatar {
    display: grid;
    place-items: center;
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: var(--r-pill);
    background: var(--accent-tint);
    color: var(--accent);
    font-weight: 700;
}

.ch-tt-who { flex: 1; min-width: 0; }
.ch-tt-host {
    display: block;
    font-weight: 700;
    font-size: var(--t-sm);
}

/* How many spots are left is the single most useful fact on the
   card — it is the reason someone is reading it. */
.ch-tt-spots {
    display: inline-block;
    margin-top: 2px;
    padding: 2px var(--s-2);
    border-radius: var(--r-pill);
    font-size: var(--t-xs);
    font-weight: 700;
}
.ch-tt-spots.is-looking { background: var(--accent-tint);  color: var(--tt-orange-deep); }
.ch-tt-spots.is-full    { background: var(--n-0);          color: var(--ink-muted); }

.ch-tt-remove {
    flex: none;
    padding: var(--s-2) var(--s-3);
    border: 1px solid var(--line-strong);
    border-radius: var(--r-sm);
    background: transparent;
    font: inherit;
    font-size: var(--t-xs);
    font-weight: 600;
    color: var(--ink-muted);
    cursor: pointer;
}
.ch-tt-remove:hover { border-color: var(--tt-bad); color: var(--tt-bad); }

.ch-tt-body { padding-top: var(--s-4); }
.ch-tt-course { margin: 0; font-weight: 600; }
.ch-tt-when   { margin: var(--s-1) 0 0; color: var(--accent); font-weight: 600; font-size: var(--t-sm); }
.ch-tt-meta   { margin: var(--s-2) 0 0; color: var(--ink-faint); font-size: var(--t-sm); }
.ch-tt-notes  {
    margin: var(--s-3) 0 0;
    padding-top: var(--s-3);
    border-top: 1px solid var(--line);
    color: var(--ink-muted);
    font-size: var(--t-sm);
}

/* ── Empty, and honest about why ──────────────────────────── */

.ch-tt-empty {
    padding: var(--s-7) var(--s-5);
    border: 1px dashed var(--line-strong);
    border-radius: var(--r-md);
    text-align: center;
}
.ch-tt-empty-head { margin: 0 0 var(--s-3); font-weight: 700; }
.ch-tt-empty-note {
    max-width: 34rem;
    margin: 0 auto;
    color: var(--ink-muted);
    font-size: var(--t-sm);
    line-height: var(--lh-body);
}

/* ── Posting ──────────────────────────────────────────────── */

.ch-tt-pair {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-4);
}

.ch-req { color: var(--tt-bad); }

.ch-field.is-off { opacity: 0.45; }

.ch-tt-mine-head { margin-top: var(--s-7); }

/* The app's toggle rows: a name, a line of explanation, a switch. */
.ch-toggle {
    display: flex;
    align-items: center;
    gap: var(--s-4);
    padding: var(--s-4) 0;
    border-top: 1px solid var(--line);
}
.ch-toggle-name { display: block; font-weight: 600; font-size: var(--t-sm); }
.ch-toggle-note {
    display: block;
    margin-top: 2px;
    color: var(--ink-muted);
    font-size: var(--t-xs);
    line-height: var(--lh-snug);
}

/* The label IS the control — the checkbox is visually hidden inside
   it, so a click anywhere on the switch toggles it and the browser
   handles keyboard and screen readers for free. The standard
   clip-rect hiding rather than display:none or visibility:hidden,
   both of which would make it unfocusable. */
.ch-switch { flex: none; cursor: pointer; display: inline-block; }
.ch-switch input {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}
.ch-switch-track {
    display: block;
    position: relative;
    width: 46px;
    height: 28px;
    border-radius: var(--r-pill);
    /* The app's own toggleOffColor / toggleOnColor. */
    background: rgba(120, 120, 128, 0.16);
    transition: background var(--dur-base) var(--ease);
}
.ch-switch-track::after {
    content: '';
    position: absolute;
    top: 2px;
    left: 2px;
    width: 24px;
    height: 24px;
    border-radius: var(--r-pill);
    background: #FFFFFF;
    box-shadow: var(--shadow-sm);
    transition: transform var(--dur-base) var(--ease);
}
.ch-switch input:checked + .ch-switch-track { background: #34C759; }
.ch-switch input:checked + .ch-switch-track::after { transform: translateX(18px); }
.ch-switch input:focus-visible + .ch-switch-track {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* A refusal, not a save. .ch-form-status is green by default. */
.ch-form-status.is-bad { color: var(--tt-bad); }

/* Visually hidden, still read aloud. */
.ch-sr {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

@media (max-width: 900px) {
    .ch-tt-layout { grid-template-columns: 1fr; gap: var(--s-6); }
}

@media (max-width: 560px) {
    .ch-segments { display: flex; width: 100%; }
    .ch-segment  { padding: var(--s-2) var(--s-2); font-size: var(--t-xs); }
    .ch-tt-pair  { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
    .ch-switch-track,
    .ch-switch-track::after { transition-duration: 0.01ms; }
}

/* ============================================================
   THE STAFF HUB  —  staff.html
   ------------------------------------------------------------
   The chooser you land on after the passphrase. It exists because
   Settings used to drop you straight into Club Match's inventory,
   which is one console of several and looked like the only one.
   ============================================================ */

.ch-staff-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--s-5);
}

.ch-staff-card {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    padding: var(--s-6);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--surface-raised);
    color: inherit;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    transition: transform var(--dur-base) var(--ease),
                box-shadow var(--dur-base) var(--ease),
                border-color var(--dur-base) var(--ease);
}

.ch-staff-card:hover {
    transform: translateY(-4px);
    border-color: var(--accent);
    box-shadow: var(--shadow-lg);
}

.ch-staff-icon {
    display: grid;
    place-items: center;
    width: 48px;
    height: 48px;
    margin-bottom: var(--s-2);
    border-radius: var(--r-sm);
    background: var(--accent-tint);
    font-size: var(--t-lg);
}

.ch-staff-name {
    font-family: var(--font-display);
    font-size: var(--t-lg);
    font-weight: 700;
}

.ch-staff-what {
    flex: 1;
    font-size: var(--t-sm);
    color: var(--ink-muted);
    line-height: var(--lh-body);
}

.ch-staff-go {
    margin-top: var(--s-3);
    font-size: var(--t-sm);
    font-weight: 700;
    color: var(--accent);
}

.ch-staff-foot {
    display: flex;
    align-items: center;
    gap: var(--s-5);
    flex-wrap: wrap;
    margin-top: var(--s-7);
    padding-top: var(--s-5);
    border-top: 1px solid var(--line);
}
.ch-staff-foot .ch-card-note { flex: 1; margin: 0; min-width: 16rem; }

@media (prefers-reduced-motion: reduce) {
    .ch-staff-card:hover { transform: none; }
}

/* "or" between the two contact fields in the join panel. A divider
   with a word on it, because two stacked inputs with no separator
   read as "fill in both". */
.ch-or {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin: var(--s-2) 0;
    font-size: var(--t-xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--ink-faint);
}
.ch-or::before,
.ch-or::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--line);
}

/* ── The membership card on the profile ───────────────────── */
.ch-tier-now {
    margin: 0 0 var(--s-4);
    font-size: var(--t-md);
    font-weight: 700;
}
.ch-tier-now-guest,
.ch-tier-now-friend { color: var(--ink-muted); }
.ch-tier-now-member { color: var(--tt-good); }
.ch-tier-now-admin  { color: var(--accent); }

/* A locked perk is stated, not hidden. Someone on a two-week
   calendar cannot see that it goes further, which makes a free
   upgrade feel like a missing feature. */
.ch-milestone.is-locked { opacity: 0.6; }
.ch-milestone.is-locked .ch-milestone-state { color: var(--ink-faint); }

/* ============================================================
   THE GATE — locked controls, and the offer behind them
   ============================================================ */

/* ── A LOCKED CONTROL IS STILL A CONTROL ──────────────────
   It is dimmed, it carries a padlock, and it is fully
   clickable — because the click is what explains the lock.

   `disabled` was the obvious choice and the wrong one: a
   disabled button cannot be focused, is skipped by screen
   readers, and swallows the very click that would have said
   why. A guest would meet a grey rectangle with no way to ask
   what it was for. See the note in gate.js.

   NOT `display: none` either. Someone who cannot see that the
   calendar goes past two weeks experiences a missing feature
   rather than an offer, and never asks for the thing that is
   free. */
.is-locked {
    opacity: 0.62;
    cursor: pointer;
}
.is-locked::after {
    content: ' 🔒';
    font-size: 0.85em;
    /* Never let the padlock be the whole affordance — the title,
       the aria-label and the panel all say it in words. */
    opacity: 0.8;
}
.is-locked:hover,
.is-locked:focus-visible {
    opacity: 0.85;
}

/* A locked area rather than a locked button: the tee time feed a
   guest cannot read, the calendar past the fourteenth day. */
.ch-gate-wall {
    display: grid;
    gap: var(--s-3);
    justify-items: center;
    padding: var(--s-6) var(--s-5);
    border: 1px dashed var(--line);
    border-radius: var(--r-lg);
    background: color-mix(in srgb, var(--accent) 5%, transparent);
    text-align: center;
}
.ch-gate-wall h3 {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--t-md);
    font-weight: 700;
}
.ch-gate-wall p {
    margin: 0;
    max-width: 34rem;
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: var(--ink-muted);
}
.ch-gate-wall-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    justify-content: center;
    margin-top: var(--s-2);
}

/* ── The refusal panel ────────────────────────────────────
   Same furniture as the join panel on purpose: someone about to
   be asked to join should recognise where they have landed. */
.ch-gate-why {
    margin: 0 0 var(--s-5);
    font-size: var(--t-sm);
    line-height: var(--lh-body);
    color: var(--ink-muted);
}

.ch-gate-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    margin-bottom: var(--s-4);
}

.ch-gate-note {
    margin: 0;
    font-size: var(--t-xs);
    line-height: var(--lh-body);
    color: var(--ink-faint);
}
