/* =============================================================================
   Native-feel primitives (S2-2 frame, 2026-10-08). Loaded by ui-light.php on every ca-ui page, after the family
   stylesheets and glass.css. A website that behaves the way an iPhone hand expects: not an app, no PWA, no
   service worker. API and markup: the header of ux.js and factory/night/2026-10-08-s2/f-frame.md.

     .ux-sheet   pull-up bottom sheet (<dialog>), centred panel from 768px
     .ux-seg     segmented control (tabs or a plain selector)
     .ux-snap    momentum scroll-snap carousel, optional dots and arrows
     .ux-tap     pressed-state feedback that needs no hover
     --ux-safe-* safe-area insets as tokens
     View Transitions between pages (named header and hero), off under reduced motion

   House tokens only. Every value a builder may want to tune is a --ux-* token below.
   ============================================================================= */
:root {
    --ux-vt-dur: 280ms;                               /* page cross-fade */
    --ux-vt-hero-dur: 420ms;                          /* the hero morph */
    --ux-vt-ease: cubic-bezier(.32, .72, 0, 1);
}
body.ca-ui {
    /* Safe areas. They read zero until the viewport meta carries viewport-fit=cover (see f-frame.md). */
    --ux-safe-top: env(safe-area-inset-top, 0px);
    --ux-safe-right: env(safe-area-inset-right, 0px);
    --ux-safe-bottom: env(safe-area-inset-bottom, 0px);
    --ux-safe-left: env(safe-area-inset-left, 0px);

    --ux-target: 48px;                                /* minimum touch target */
    --ux-ease: cubic-bezier(.32, .72, 0, 1);          /* the iOS sheet curve */
    --ux-dur: 380ms;
    --ux-dur-fast: 140ms;
    --ux-press-scale: .97;
    --ux-press-opacity: .78;
    --ux-tap-tint: color-mix(in srgb, var(--ui-accent) 16%, transparent);

    --ux-scrim: color-mix(in srgb, var(--obsidian) 42%, transparent);
    --ux-sheet-bg: var(--ui-paper);
    --ux-sheet-radius: 22px;
    --ux-sheet-max: 88dvh;                            /* tallest a phone sheet gets */
    --ux-sheet-w: 560px;                              /* width of the centred panel from 768px */
    --ux-sheet-pad: clamp(1.25rem, 5vw, 1.75rem);
    --ux-grip: color-mix(in srgb, var(--obsidian) 22%, transparent);

    --ux-seg-bg: var(--ui-tint);
    --ux-seg-thumb: var(--ui-paper);
    --ux-seg-radius: 999px;

    --ux-snap-item: 85%;                              /* card width on a phone: the next card peeks */
    --ux-snap-gap: 12px;
    --ux-snap-pad: clamp(1.25rem, 5vw, 2rem);         /* first card lines up with the page gutter */
    --ux-dot: var(--ui-line);
    --ux-dot-on: var(--ui-ink);

    -webkit-tap-highlight-color: var(--ux-tap-tint);
}

/* ---- Tap feedback ----------------------------------------------------------------------------------------
   :active works on touch and mouse alike, so nothing depends on hover. Opt in with .ux-tap; the primitives below
   and the shared phone bar use it already. */
body.ca-ui :is(.ux-tap, .ux-seg > *, .ux-snap-dot, .ux-snap-arrow, .ux-sheet-x, .ui-mbar-btn) {
    -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
@media (prefers-reduced-motion: no-preference) {
    body.ca-ui :is(.ux-tap, .ux-snap-arrow, .ux-sheet-x, .ui-mbar-btn) { transition: transform var(--ux-dur-fast) ease, opacity var(--ux-dur-fast) ease; }
}
body.ca-ui :is(.ux-tap, .ux-snap-arrow, .ux-sheet-x, .ui-mbar-btn):active { transform: scale(var(--ux-press-scale)); opacity: var(--ux-press-opacity); }
body.ca-ui .ux-seg > *:active { opacity: var(--ux-press-opacity); }
@media (prefers-reduced-motion: reduce) {
    body.ca-ui :is(.ux-tap, .ux-snap-arrow, .ux-sheet-x, .ui-mbar-btn):active { transform: none; }
}

/* ---- Scroll lock without a layout jump --------------------------------------------------------------------
   ux.js adds html.ux-lock while a sheet is open. The gutter stays reserved, so nothing moves sideways. */
html.ux-lock { overflow: hidden; }
html.ux-lock.ux-lock-gutter { scrollbar-gutter: stable; }          /* only when a classic scrollbar was there */
html.ux-lock.ux-lock-pad { padding-right: var(--ux-sbw, 0px); }   /* engines without scrollbar-gutter */

/* ---- Bottom sheet ---------------------------------------------------------------------------------------- */
body.ca-ui dialog.ux-sheet {
    position: fixed; inset: auto 0 0 0; margin: 0; width: 100%; max-width: 100%; max-height: var(--ux-sheet-max);
    padding: 0; border: 0; border-radius: var(--ux-sheet-radius) var(--ux-sheet-radius) 0 0;
    background: var(--ux-sheet-bg); color: var(--ui-ink); box-shadow: 0 -10px 40px color-mix(in srgb, var(--obsidian) 16%, transparent);
    font-family: var(--ui-font-body, 'Jost', sans-serif); overflow: hidden;
    transform: translateY(calc(100% + 24px)); }
body.ca-ui dialog.ux-sheet[open] { display: flex; flex-direction: column; }
body.ca-ui dialog.ux-sheet.is-open { transform: translateY(var(--ux-drag, 0px)); }
body.ca-ui dialog.ux-sheet::backdrop { background: var(--ux-scrim); opacity: 0; }
body.ca-ui dialog.ux-sheet.is-open::backdrop { opacity: calc(1 - var(--ux-drag-p, 0)); }
@media (prefers-reduced-motion: no-preference) {
    body.ca-ui dialog.ux-sheet { transition: transform var(--ux-dur) var(--ux-ease); }
    body.ca-ui dialog.ux-sheet::backdrop { transition: opacity var(--ux-dur) ease; }
    body.ca-ui dialog.ux-sheet.is-dragging, body.ca-ui dialog.ux-sheet.is-dragging::backdrop { transition: none; }
}
body.ca-ui .ux-sheet-grip { flex: none; display: grid; place-items: center; height: 28px; cursor: grab; touch-action: none; }
body.ca-ui .ux-sheet-grip::before { content: ""; width: 40px; height: 5px; border-radius: 999px; background: var(--ux-grip); }
body.ca-ui .ux-sheet-head { flex: none; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: 0 var(--ux-sheet-pad) .75rem; touch-action: none; }
body.ca-ui .ux-sheet-head :is(h2, h3, .ux-sheet-title) { margin: 0; font-family: var(--ui-font-body, 'Jost', sans-serif) !important;
    font-size: 20px; font-weight: 600 !important; line-height: 1.25; letter-spacing: 0 !important; color: var(--ui-ink); }
body.ca-ui .ux-sheet-x { flex: none; display: grid; place-items: center; width: var(--ux-target); height: var(--ux-target); margin-right: -.5rem;
    border: 0; border-radius: 50%; background: transparent; color: var(--ui-ink); cursor: pointer; }
body.ca-ui .ux-sheet-x svg { width: 20px; height: 20px; }
/* The close button takes focus when a sheet opens: show the ring after keyboard use only (qa S2 LOW 10, m-hubs). */
body.ca-ui .ux-sheet-x:focus:not(:focus-visible) { outline: none !important; box-shadow: none !important; }
body.ca-ui .ux-sheet-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
    padding: 0 var(--ux-sheet-pad) calc(1.25rem + var(--ux-safe-bottom)); font-size: 16px; line-height: 1.6; }
body.ca-ui .ux-sheet-foot { flex: none; display: flex; gap: .75rem; padding: .75rem var(--ux-sheet-pad) calc(.75rem + var(--ux-safe-bottom));
    border-top: 1px solid var(--ui-line); background: var(--ux-sheet-bg); }
body.ca-ui .ux-sheet-foot > * { flex: 1 1 0; min-width: 0; min-height: var(--ux-target); }
/* Fields inside a sheet: light, 16px (iOS zooms the page on focus below 16px), 48px tall. The legacy stylesheet
   paints inputs dark; this resets them. Use type="date|tel|email|number" and inputmode for the native keyboard. */
body.ca-ui .ux-sheet :is(input:not([type="checkbox"]):not([type="radio"]):not([type="range"]), select, textarea) {
    width: 100%; min-height: var(--ux-target); padding: .6rem .85rem; box-sizing: border-box;
    border: 1px solid color-mix(in srgb, var(--obsidian) 22%, transparent); border-radius: var(--ui-radius-sm);
    background: var(--ui-paper); color: var(--ui-ink); font: 400 16px/1.4 var(--ui-font-body, 'Jost', sans-serif); letter-spacing: 0; text-transform: none; }
body.ca-ui .ux-sheet :is(input, select, textarea):focus-visible { outline: 2px solid var(--ui-accent); outline-offset: 1px; border-color: var(--ui-ink); }
body.ca-ui .ux-sheet:has(.ux-sheet-foot) .ux-sheet-body { padding-bottom: 1.25rem; }
@media (min-width: 768px) {
    /* A centred panel on desktop and tablet: no grip, no drag, same markup. */
    body.ca-ui dialog.ux-sheet { inset: 0; margin: auto; width: min(var(--ux-sheet-w), calc(100vw - 4rem)); max-height: min(80vh, 760px); height: fit-content;
        border-radius: var(--ux-sheet-radius); box-shadow: var(--ui-shadow); transform: translateY(16px) scale(.98); opacity: 0; }
    body.ca-ui dialog.ux-sheet.is-open { transform: none; opacity: 1; }
    body.ca-ui .ux-sheet-grip { display: none; }
    body.ca-ui .ux-sheet-head { padding-top: 1.25rem; touch-action: auto; }
    body.ca-ui .ux-sheet-body { padding-bottom: 1.5rem; }
}
@media (min-width: 768px) and (prefers-reduced-motion: no-preference) {
    body.ca-ui dialog.ux-sheet { transition: transform var(--ux-dur) var(--ux-ease), opacity 220ms ease; }
}
@media (hover: hover) and (pointer: fine) {
    body.ca-ui .ux-sheet-x:hover { background: var(--ui-tint); }
}

/* ---- Segmented control ----------------------------------------------------------------------------------- */
body.ca-ui .ux-seg { position: relative; isolation: isolate; display: inline-grid; grid-auto-flow: column; grid-auto-columns: minmax(max-content, 1fr);
    gap: 0; max-width: 100%; padding: 4px; border-radius: var(--ux-seg-radius); background: var(--ux-seg-bg);
    overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
body.ca-ui .ux-seg::-webkit-scrollbar { display: none; }
body.ca-ui .ux-seg.ux-seg--block { display: grid; width: 100%; }
body.ca-ui .ux-seg > :is(button, a, label) { position: relative; z-index: 1; display: inline-flex; align-items: center; justify-content: center;
    min-height: calc(var(--ux-target) - 8px); padding: 0 1.1rem; border: 0; border-radius: var(--ux-seg-radius); background: transparent;
    font: 500 15px/1.2 var(--ui-font-body, 'Jost', sans-serif); letter-spacing: .01em; text-transform: none; white-space: nowrap;
    color: var(--ui-ink-2); text-decoration: none; cursor: pointer; }
body.ca-ui .ux-seg > [aria-selected="true"], body.ca-ui .ux-seg > [aria-pressed="true"], body.ca-ui .ux-seg > [aria-current] { color: var(--ui-ink); font-weight: 600; }
/* Without ux.js the selected segment carries its own thumb; with it, one thumb slides between segments. */
body.ca-ui .ux-seg:not(.is-ready) > :is([aria-selected="true"], [aria-pressed="true"], [aria-current]) { background: var(--ux-seg-thumb); box-shadow: 0 1px 3px color-mix(in srgb, var(--obsidian) 12%, transparent); }
body.ca-ui .ux-seg.is-ready::before { content: ""; position: absolute; z-index: 0; top: 4px; bottom: 4px; left: 0; width: var(--ux-seg-w, 0px);
    transform: translateX(var(--ux-seg-x, 0px)); border-radius: var(--ux-seg-radius); background: var(--ux-seg-thumb);
    box-shadow: 0 1px 3px color-mix(in srgb, var(--obsidian) 12%, transparent), 0 4px 12px color-mix(in srgb, var(--obsidian) 6%, transparent); }
@media (prefers-reduced-motion: no-preference) {
    body.ca-ui .ux-seg.is-ready.is-moving::before { transition: transform 300ms var(--ux-ease), width 300ms var(--ux-ease); }
}
@media (hover: hover) and (pointer: fine) {
    body.ca-ui .ux-seg > :not([aria-selected="true"]):not([aria-pressed="true"]):hover { color: var(--ui-ink); }
}

/* ---- Momentum scroll-snap carousel ------------------------------------------------------------------------
   Native momentum and snap; overscroll is contained so the carousel's end never turns into a back navigation,
   and vertical page scroll passes straight through (no scroll trap). */
body.ca-ui .ux-snap { display: grid; grid-auto-flow: column; grid-auto-columns: var(--ux-snap-item); gap: var(--ux-snap-gap);
    overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scroll-snap-type: x mandatory;
    padding-inline: var(--ux-snap-pad); scroll-padding-inline: var(--ux-snap-pad); scrollbar-width: none; }
body.ca-ui .ux-snap::-webkit-scrollbar { display: none; }
body.ca-ui .ux-snap > * { min-width: 0; scroll-snap-align: start; }
@media (min-width: 768px) { body.ca-ui .ux-snap:not(.ux-snap--full) { --ux-snap-item: calc((100% - 2 * var(--ux-snap-gap)) / 3); } }
body.ca-ui .ux-snap.ux-snap--one > * { scroll-snap-stop: always; }    /* one card per swipe, gallery style */
body.ca-ui .ux-snap.ux-snap--full { --ux-snap-item: 100%; --ux-snap-gap: 0px; --ux-snap-pad: 0px; }
@media (prefers-reduced-motion: no-preference) { body.ca-ui .ux-snap { scroll-behavior: smooth; } }
body.ca-ui .ux-snap:focus-visible { outline: 2px solid var(--ui-accent); outline-offset: 4px; }
body.ca-ui .ux-snap-ui { display: flex; align-items: center; justify-content: center; gap: .5rem; padding-inline: var(--ux-snap-pad); }
body.ca-ui .ux-snap-dots { display: flex; flex-wrap: wrap; justify-content: center; }
body.ca-ui .ux-snap-dot { display: grid; place-items: center; width: 24px; height: var(--ux-target); padding: 0; border: 0; background: transparent; cursor: pointer; }
body.ca-ui .ux-snap-dot::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: var(--ux-dot); }
body.ca-ui .ux-snap-dot[aria-current="true"]::before { width: 20px; background: var(--ux-dot-on); }
@media (prefers-reduced-motion: no-preference) { body.ca-ui .ux-snap-dot::before { transition: width 240ms var(--ux-ease), background 240ms ease; } }
/* Arrows only where a mouse is the pointer: a phone swipes. */
body.ca-ui .ux-snap-arrow { display: none; }
@media (hover: hover) and (pointer: fine) {
    body.ca-ui .ux-snap-arrow { display: grid; place-items: center; width: var(--ux-target); height: var(--ux-target); padding: 0; flex: none;
        border: 1px solid color-mix(in srgb, var(--obsidian) 25%, transparent); border-radius: 50%; background: var(--ui-paper); color: var(--ui-ink); cursor: pointer; }
    body.ca-ui .ux-snap-arrow:hover { border-color: var(--ui-ink); }
    body.ca-ui .ux-snap-arrow[disabled] { opacity: .35; cursor: default; }
    body.ca-ui .ux-snap-arrow svg { width: 18px; height: 18px; }
}

/* ---- Cross-document View Transitions -----------------------------------------------------------------------
   Same-origin navigations cross-fade; the header holds still and the hero morphs. ui-light.php names the
   header and hero only for the length of a transition (a permanent name would make the header a backdrop root
   and flatten its glass). Engines without the feature navigate as before. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
    @view-transition { navigation: none; }
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
::view-transition-old(root) { animation: var(--ux-vt-dur) ease both ux-vt-out; }
::view-transition-new(root) { animation: var(--ux-vt-dur) var(--ux-vt-ease) both ux-vt-in; }
::view-transition-group(ca-header) { animation: none; z-index: 2; }
::view-transition-old(ca-header) { display: none; }
::view-transition-new(ca-header) { animation: none; }
::view-transition-group(ca-hero) { animation-duration: var(--ux-vt-hero-dur); animation-timing-function: var(--ux-vt-ease); }
::view-transition-old(ca-hero), ::view-transition-new(ca-hero) { height: 100%; object-fit: cover; overflow: clip; }
@keyframes ux-vt-out { to { opacity: 0; } }
@keyframes ux-vt-in { from { opacity: 0; transform: translateY(10px); } }
