/* ==========================================================================
   shell.css - shared shell layer for the mockup / demo pages.
    Holds the .shell token block, the base element rules, reusable .ui-* content
    components, the shell navigation hooks, and the .shell__* frame/ad slots.
    It is the same layer on
   every page: page-specific CSS belongs in the page, not here.
   Load order: after the Tailwind Play CDN script (see template/partials/shell-head.php).
   Origin: split out of camera-database-demo-var-c.html so pages stop
   carrying a 500-line copy each.
   ========================================================================== */

*,
*::before,
*::after {
    box-sizing: border-box;
    border-width: 0;
    border-style: solid;
}

/* Document-level defaults */
html {
    color-scheme: light dark;
    background-color: light-dark(#f2f2f2, #000000);
    scrollbar-gutter: stable;
    scroll-behavior: smooth;
}

html[data-theme="light"] { color-scheme: light; }
html[data-theme="dark"]  { color-scheme: dark; }

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
}

/* Shared design tokens */
.shell {
    --sf-price: #f9475b;
    --sf-price-deal: #ff4400;
    --sf-buy: #ff6a00;
    --sf-buy-dark: #e85d00;
    --sf-buy-soft: #fff1e6;
    --sf-save: #16a34a;

    --sf-amber: #ffb400;
    --sf-teal: #0f9d8f;
    --sf-heading: #0f172a;
    --sf-link: #129fe5;

    --sf-topbar-bg: light-dark(var(--sf-fill), var(--sf-surface));

    --sf-sp-1: 4px;
    --sf-sp-2: 8px;
    --sf-sp-3: 12px;
    --sf-sp-4: 16px;
    --sf-sp-5: 20px;
    --sf-sp-6: 24px;
    --sf-sp-8: 32px;
    --sf-sp-10: 40px;
    --sf-sp-12: 48px;

    --sf-gutter: var(--sf-sp-6);

    --sf-radius-sm: 10px;
    --sf-radius: 14px;
    --sf-radius-lg: 20px;

    --sf-shadow-xs: 0 0 4px rgba(15, 23, 42, .06),
        0 1px 2px rgba(15, 23, 42, .05),
        0 2px 8px -2px rgba(15, 23, 42, .07);
    --sf-shadow-sm: 0 0 6px rgba(15, 23, 42, .08),
        0 1px 2px rgba(15, 23, 42, .05),
        0 6px 16px -6px rgba(15, 23, 42, .12);
    --sf-shadow: 0 2px 4px rgba(15, 23, 42, .03), 0 14px 32px -14px rgba(15, 23, 42, .16);
    --sf-shadow-lg: 0 4px 8px rgba(15, 23, 42, .04), 0 28px 56px -20px rgba(15, 23, 42, .22);

    --sf-container: 1360px;
    --sf-header-h: 76px;
    --sf-ease: cubic-bezier(.2, .7, .3, 1);

    --sf-brand: #2B8DDE;
    --sf-brand-dark: #1f7ac6;
    --sf-brand-deep: #1760a5;
    --sf-brand-soft: light-dark(#e8f2fb, #0d3050);
    --sf-brand-tint: light-dark(#f2f8fd, #0a2333);

    --sf-ink: light-dark(#020617, #eef1f4);
    --sf-ink-2: light-dark(#2e3a4e, #d8dde3);
    --sf-muted: light-dark(#4a5870, #9aa1a9);
    --sf-muted-2: #8192a8;

    --sf-page: light-dark(#f2f2f2, #111111);
    --sf-surface: light-dark(#ffffff, #111111);
    --sf-surface-2: light-dark(#f8fafc, #1c1c1c);

    --sf-card: light-dark(#ffffff, #1c1c1c);
    --sf-plain: light-dark(#ffffff, #000000);
    --sf-content: var(--sf-surface);

    --sf-fill: light-dark(#f1f5f9, #202020);
    --sf-line: light-dark(#e9edf2, #2a2a2a);
    --sf-line-strong: light-dark(#dbe2ea, #383838);

    --shell-well: light-dark(#e6e8ec, #0d0d0d);
    --shell-link: light-dark(var(--sf-brand-deep), #8fd0f5);
    --shell-glow-1: color-mix(in srgb, var(--sf-brand) 60%, transparent);
    --shell-glow-2: color-mix(in srgb, var(--sf-brand) 70%, transparent);
    --shell-hair: light-dark(rgba(15, 23, 42, .07), rgba(255, 255, 255, .08));
    --shell-blur: saturate(180%) blur(20px);
    --shell-glass: light-dark(rgba(255, 255, 255, .78), rgba(17, 17, 17, .72));
    --shell-ease: var(--sf-ease);
    --shell-fs-sm: 14px;
    --shell-fs-md: 15px;
}

/* Shell base styles */
.shell {
    background: var(--sf-page);
    color: var(--sf-ink);
    font-size: 15px;
    line-height: 1.65;
    font-family:
        system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
        'PingFang HK', 'PingFang TC',
        'Microsoft JhengHei UI', 'Microsoft JhengHei',
        'Noto Sans HK', 'Noto Sans TC', 'Heiti TC',
        'Helvetica Neue', Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    margin: 0;
    padding: 0;
    accent-color: var(--sf-brand);
}

/* THE TOPBAR'S BED, named once (2026-10-20, user: "for the login page, please
   make the background color as shell-topbar"). The utility strip paints
   `light-dark(var(--sf-fill), var(--sf-surface))`; the sign-in page now wears the
   SAME colour as its bed, so the topbar reads as part of the page instead of a
   separate band floating above a differently tinted one.
   ⚠ It must come AFTER `.shell` above: the <body> carries BOTH classes, so the
   tie (0,1,0 each) is decided by source order, not by specificity. */
.sf-topbar-bed {
    background: light-dark(var(--sf-fill), var(--sf-surface));
}

.shell :where(h1, h2, h3, h4) {
    color: var(--sf-ink);
    font-weight: 700;
    line-height: 1.35;
    letter-spacing: -.01em;
}

.shell :where(img) {
    max-width: 100%;
    height: auto;
}

.shell a {
    text-decoration: none;
}

/* The UA margins a Preflight build would zero for us. `figure` is in the list
   because its UA value is `1em 40px`: without it an in-article <figure>
   (readnews.php's .rn-article-fig) keeps a 40px inset on both sides and stops
   lining up with the prose it sits in. */
:where(.shell) :where(p, ul, ol, figure, h1, h2, h3, h4) {
    margin: 0;
}

/* Shared storefront components */
@keyframes shell-mega-in {
    from { opacity: 0; transform: translateY(-8px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* THE RHYTHM PAIR - spacing for a view's top-level blocks. The view's own
    zero-specificity margin reset (further down) cannot override these values.

   A phone gets one step less air than a desktop: between two sections that is
   20 (the view's gap) + 24 = 44px, and 20 + 32 = 52px from 768px up. `.ui-lead`
   is the page's LEAD block (the channel band, the catalogue head); it is NOT
   part of the section rhythm, so its number is its own and is not stepped.

   The width step sits HERE, beside the value it steps, deliberately: while this
   class's only surviving rule lived in a `@media (min-width: 768px)` at the END
   of the file, a phone got no section margin at all (2026-10-15). Keep the two
   together. */
.ui-section {
    margin-bottom: var(--sf-sp-6);
}

@media (min-width: 768px) {
    .ui-section {
        margin-bottom: var(--sf-sp-8);
    }
}

.ui-lead {
    margin-bottom: var(--sf-sp-4);
}

.ui-select {
    padding: 11px var(--sf-sp-4);
    border: 1px solid transparent;
    border-radius: var(--sf-radius);
    background: var(--sf-fill);
    color: var(--sf-ink);
    font-family: inherit;
    font-size: 14px;
    line-height: 1.2;
    transition: background .18s var(--sf-ease), border-color .18s var(--sf-ease),
        box-shadow .18s var(--sf-ease);
}

.ui-select:focus {
    background: var(--sf-surface);
    border-color: var(--sf-brand);
    outline: none;
    box-shadow: 0 0 0 4px var(--sf-brand-soft);
}

/* THE PRODUCT CARD (see --sf-card). The pages carry this class instead of the
   Tailwind `bg-surface` it replaced: a utility is injected AFTER this sheet, so
   at equal specificity it would win here and the dark value would never apply. */
.ui-card {
    background: var(--sf-card);
}

.ui-panel {
    background: var(--sf-surface);
    border-radius: var(--sf-radius-lg);
    box-shadow: var(--sf-shadow-xs);
    overflow: hidden;
}

.ui-crumbs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sf-sp-2);
    margin-bottom: var(--sf-sp-5);
    font-size: 13px;
    color: var(--sf-muted);
}

.ui-crumbs a:hover {
    color: var(--sf-brand);
}

.ui-crumbs__sep {
    color: var(--sf-muted-2);
}

.ui-crumbs__current {
    color: var(--sf-ink);
    font-weight: 600;
}

/* The real shop gets anchor colour inheritance from Tailwind Preflight
   (`a { color: inherit }`), which these pages switch OFF - so a crumb anchor
   would fall back to the browser's link colour instead of the muted colour
   above. :where() holds the specificity at (0,0,1), exactly Preflight's, so a
   Tailwind text utility on a crumb still wins. Do NOT "simplify" this to
    `.ui-crumbs a` - that is (0,2,1) and would outrank the utilities. */
:where(.ui-crumbs) a {
    color: inherit;
}

.shell-hidden,
.ui-hidden {
    display: none !important;
}

/* --------------------------------------------------------------------------
   THE SIGN-IN PAGE (.sf-auth). Its only consumer is the site-wide sign-in
   (SITE_LOGIN_PATH, app/System/Views/site/login.php). Token-based, so both
   themes follow the shell's own palette; real rules rather than Tailwind
   utilities because the box is a component, and because these pages are built
   without Preflight (a utility stack would carry no reset of its own).
   storefront.css §16 carries the same block for the shop's chrome - keep the
   two in step if the box changes. ONE DELIBERATE DIVERGENCE: this copy's box reads
   --sf-card (it sits on a shell page's tinted bed); §16 keeps --sf-surface, which
   is right for the storefront's own bed.
   -------------------------------------------------------------------------- */
.sf-auth {
    position: relative;                 /* the halo below anchors to this */
    display: flex;
    flex-direction: column;             /* card, then the promise band */
    align-items: center;
    justify-content: center;
    gap: var(--sf-sp-6);
    min-height: 60vh;
    padding-block: var(--sf-sp-4);      /* phone: 16px */
    padding-inline: var(--sf-gutter);   /* the shell's own gutter, so the card never touches the edge */
}

/* The width steps sit beside the value they step (same shape as `.ui-section`):
   phone 16, tablet 24, desktop 32. */
@media (min-width: 768px) {
    .sf-auth {
        padding-block: var(--sf-sp-6);
    }
}

@media (min-width: 1024px) {
    .sf-auth {
        padding-block: var(--sf-sp-8);
    }
}

/* The page's depth: a soft halo behind the card. It reads because the page sits on
   the tinted bed (site.php now sets NO shell modifier, so the body keeps --sf-page)
   while the card carries --sf-card - the one token that stands ABOVE the page in
   BOTH themes. */
.sf-auth__glow {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    z-index: -1;
    width: min(560px, 92vw);
    height: min(560px, 92vw);
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--sf-brand-soft), transparent);
    pointer-events: none;
}

.sf-auth__box {
    width: 100%;
    max-width: 440px;
    background: var(--sf-card);
    border-radius: var(--sf-radius-lg);
    box-shadow: var(--sf-shadow);
    padding: var(--sf-sp-8);
}

.sf-auth__head {
    margin-bottom: var(--sf-sp-8);
    text-align: center;
}

.sf-auth__title {
    font-size: 24px;
    font-weight: 800;
    color: var(--sf-ink);
}

.sf-auth__sub {
    margin-top: 6px;
    font-size: 14px;
    color: var(--sf-muted);
}

.sf-auth__alert {
    margin-bottom: var(--sf-sp-4);
    padding: var(--sf-sp-3) var(--sf-sp-4);
    border-radius: var(--sf-radius);
    background: light-dark(#fdecec, #3a1f1f);
    color: light-dark(#8f2b28, #f3b0ad);
    font-size: 14px;
}

.sf-auth__form {
    display: grid;
    gap: var(--sf-sp-4);
}

.sf-auth__field {
    display: grid;
    gap: 6px;
}

.sf-auth__label {
    font-size: 13px;
    font-weight: 600;
    color: var(--sf-ink-2);
}

.sf-auth__input {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--sf-line);
    border-radius: var(--sf-radius);
    background: var(--sf-surface);
    color: var(--sf-ink);
    font: inherit;
    font-size: 14px;
}

.sf-auth__input:focus-visible {
    outline: 2px solid var(--sf-brand);
    outline-offset: 1px;
    border-color: var(--sf-brand);
}

.sf-auth__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sf-sp-3);
}

.sf-auth__check {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--sf-ink-2);
    cursor: pointer;
}

.sf-auth__check input {
    width: 16px;
    height: 16px;
    accent-color: var(--sf-brand);
}

.sf-auth__link {
    font-size: 14px;
    font-weight: 600;
    color: var(--sf-brand-dark);
}

.sf-auth__btn {
    width: 100%;
    padding: 12px 16px;
    border: 0;
    border-radius: var(--sf-radius);
    background: var(--sf-brand);
    color: #fff;
    font: inherit;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    /* inline-flex so the BUSY SPINNER can sit beside the label (2026-10-20: the
       form submits through fetch, so the button has a visible in-flight state). */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    transition: background .18s var(--sf-ease), opacity .18s var(--sf-ease);
}

.sf-auth__btn:hover {
    background: var(--sf-brand-dark);
}

/* THE BUSY STATE. `aria-busy` is set by shell.js's [data-auth-form] block, which
   also swaps the label for the form's data-auth-busy text. The spinner is a
   ::before, so no extra markup is needed in the button. */
.sf-auth__btn[aria-busy="true"] {
    opacity: .78;
    cursor: progress;
}

.sf-auth__btn[aria-busy="true"]::before {
    content: '';
    width: 14px;
    height: 14px;
    flex: none;
    border: 2px solid rgba(255, 255, 255, .45);
    border-top-color: #fff;
    border-radius: 50%;
    animation: sf-auth-spin .7s linear infinite;
}

@keyframes sf-auth-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .sf-auth__btn[aria-busy="true"]::before { animation: none; }
}

.sf-auth__alt {
    margin-top: var(--sf-sp-6);
    text-align: center;
    font-size: 14px;
    color: var(--sf-muted);
}

.sf-auth__alt a {
    color: var(--sf-brand-dark);
    font-weight: 600;
}

/* --- SOCIAL SIGN-IN (2026-10-20, user: "add login with facebook & login with
   google in the login page professionally" → "please make facebok & good
   vertically. also, please make the facebook login more facebook like") ------
   A hairline divider with its label centred in it, then the two brand buttons,
   ONE PER ROW (vertical - the user asked for it, and it is what both brands'
   own sign-in buttons look like).
   The two are deliberately NOT the same shape: Facebook wears its own filled
   blue (that IS the brand, and its glyph is drawn with currentColor so it turns
   white on the fill), while Google keeps the white outlined button with the
   four-colour G - Google's own guideline is the white button, so its brand
   colour lives in the glyph, not in the plate. */
.sf-auth__divider {
    display: flex;
    align-items: center;
    gap: var(--sf-sp-3);
    margin: var(--sf-sp-6) 0 var(--sf-sp-4);
    font-size: 12px;
    line-height: 1;
    color: var(--sf-muted-2);
    white-space: nowrap;
}

.sf-auth__divider::before,
.sf-auth__divider::after {
    content: '';
    flex: 1 1 auto;
    height: 1px;
    background: var(--sf-line);
}

.sf-auth__socials {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sf-sp-3);
}

.sf-auth__social {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 9px;
    width: 100%;
    height: 44px;
    padding-inline: 14px;
    border: 1px solid var(--sf-line-strong);
    border-radius: var(--sf-radius);
    background: var(--sf-surface);
    color: var(--sf-ink);
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: background .18s var(--sf-ease), border-color .18s var(--sf-ease),
        color .18s var(--sf-ease);
}

.sf-auth__social svg {
    width: 18px;
    height: 18px;
    flex: none;
}

.sf-auth__social:hover {
    background: var(--sf-surface-2);
    border-color: var(--sf-brand);
    color: var(--sf-brand-dark);
}

/* FACEBOOK'S OWN BUTTON: the filled brand blue with a white glyph - the one
   social button that is allowed a fill, because the fill IS the brand. */
.sf-auth__social--fb {
    background: #1877f2;
    border-color: #1877f2;
    color: #fff;
}

.sf-auth__social--fb:hover {
    background: #166fe5;
    border-color: #166fe5;
    color: #fff;
}

/* No fill: the glyph IS the button, so the box fits it. Hover is colour only -
   a background would put the plate back. The glyph is 24px, the same step the
   header's action icons use. */
.shell__burger {
    display: none;
    align-items: center;
    justify-content: center;
    /* width: 32px; */
    height: 32px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sf-ink);
    cursor: pointer;
}

/* Beats the svg's own width/height attributes. 26px since 2026-10-20 ("lets make
   the header icon a little bigger") - the same step the header's action glyphs
   moved to, so the row still reads as one set. */
.shell__burger svg {
    width: 26px;
    height: 26px;
}

.shell__burger:hover {
    color: var(--sf-brand-dark);
}

/* The drawer's dark-mode switch. A REAL component rather than a utility stack,
   because these pages run with Tailwind Preflight OFF, which breaks both halves
   of the utility version:
     - the knob is an `::after`, and `after:*` utilities rely on Preflight's
       `--tw-content: ''` default, so without it the pseudo element computes
       `content: none` and never renders (a bare pill, no knob);
     - a bare <button> keeps the UA's native bevel (`border: 2px outset` +
       `appearance: auto`), which paints a 3D frame around the pill.
   State is the switch's own aria-checked, which shell.js writes - so no class
   is needed and the announced and visible states cannot drift. The knob slides
   with inset-inline-start (not translate), so an RTL page needs no second rule. */
.shell-switch {
    position: relative;
    flex: none;
    width: 46px;
    height: 26px;
    padding: 0;
    border: 0;
    appearance: none;
    -webkit-appearance: none;
    border-radius: 999px;
    background: var(--sf-line-strong);
    cursor: pointer;
    transition: background-color .18s var(--sf-ease);
}

.shell-switch::after {
    content: "";
    position: absolute;
    top: 3px;
    inset-inline-start: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: var(--sf-shadow-xs);
    transition: inset-inline-start .18s var(--sf-ease);
}

.shell-switch[aria-checked="true"] {
    background: var(--sf-brand);
}

.shell-switch[aria-checked="true"]::after {
    inset-inline-start: calc(100% - 23px);
}

/* Moot while the chrome is ring-free (see `#shell-head :focus-visible`), kept
   for the day a ring comes back: it restates the pill radius that the shared
   ring's 6px would otherwise flatten. */
.shell .shell-switch:focus-visible {
    border-radius: 999px;
}

/* Active navigation state.

   The row is the UNDERLINE tab strip at every width: brand text + the 3px brand
   underline, plus the "one highlight at a time" hover rules. Nothing here is
   width-scoped - a mobile pill shape was tried and reverted on 2026-10-13
   ("revert the pill shape"), so the phone shows the flat text row again. The
   tab's class string lives in ONE place, $shellNavTab in the shell partial, so
   both copies of the row stay in step. */
.shell__nav-link[aria-current="page"] {
    color: var(--sf-brand);
    border-block-end-color: var(--sf-brand);
}

.shell__nav:hover .shell__nav-link[aria-current="page"] {
    color: var(--sf-ink-2);
    border-block-end-color: transparent;
}

.shell__nav:hover .shell__nav-link[aria-current="page"]:hover {
    color: var(--sf-brand);
    border-block-end-color: var(--sf-brand);
}

.shell .rounded-lg.shadow-lg {
    border-radius: var(--sf-radius-lg);
    box-shadow: var(--sf-shadow-sm);
    border: 0;
}

.shell .dark\:bg-white {
    background-color: var(--sf-surface);
}

/* THE CONTENT PAIR - the axis the CONTENT varies on (see --sf-content).
   `.shell--plain` swaps the pair to the plain one: white in light, black in
   dark, with no surface tint on the content.
   It moves a TOKEN, not a background, so nothing fights over specificity: the
   sheet below reads the token, and the site reads the same token through
   .shell--flush. That is what lets the two axes combine freely instead of
   needing a class per pairing - and it is why both switches are shell
   modifiers: they are two settings on the same frame, not a component.
   The `.shell.` half is (0,2,0), so it beats the token default at (0,1,0).

   It ALSO paints the SITE with that pair, so `shell--plain` on its own is
   already a plain page - body and content both #ffffff / #000000. `--flush`
   then only matters for the other pairing, where the site keeps the surface
   tint (#ffffff / #111111) instead of going plain. On a page with BOTH classes
   the two rules agree on the colour and the flush one (later in this file,
   same specificity) is the declaration that applies. */
.shell.shell--plain {
    --sf-content: var(--sf-plain);
    background: var(--sf-plain);
}

/* Shell background modifiers */
.shell.shell--flush {
    background: var(--sf-content);
}

/* Shell layout and chrome */
.shell .ad {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.shell .ad__box {
    position: relative;
    width: 100%;
    min-height: 0;
    background: var(--shell-well);
    box-shadow: inset 0 0 0 1px var(--shell-hair);
    overflow: hidden;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    place-items: center;
    border-radius: 0;
}

.shell .ad__inner {
    position: absolute;
    inset: 0;
}

.shell .ad--wide .ad__box {
    width: 100%;
    max-width: 970px;
    margin-inline: auto;
    aspect-ratio: 970 / 250;
}

.shell .ad--rail .ad__box,
.shell .ad--half .ad__box {
    width: 100%;
    max-width: 300px;
    margin-inline: auto;
}

.shell .ad--rail .ad__box { aspect-ratio: 300 / 250; }
.shell .ad--half .ad__box { aspect-ratio: 300 / 600; }

.shell__inner,
.shell__sheet {
    inline-size: 100%;
    max-inline-size: var(--sf-container);
    margin-inline: auto;
    padding-inline: var(--sf-gutter);
}

.shell__sheet {
    background: var(--sf-content);
    padding-block: var(--sf-sp-6) var(--sf-sp-8);
}

.shell__grid {
    display: grid;
    gap: var(--sf-sp-6);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

.shell__content {
    min-width: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sf-sp-5);
    container: content / inline-size;
}

.shell__rail {
    order: 2;
    display: grid;
    gap: var(--sf-sp-5);
    align-content: start;
}

.shell__rail[hidden] { display: none; }

.shell__view {
    display: grid;
    gap: var(--sf-sp-5);
}

.shell__view[hidden] { display: none; }

/* The view's gap already spaces the children, so their default margins would
    STACK on it. `:where()` keeps this reset at zero specificity: a component
    margin (crumbs, sections, or a page utility) can win regardless of load order.
    In particular, `.ui-section`'s sp-6 phone / sp-8 desktop rhythm is never
    erased by this reset. */
:where(.shell__view > *) { margin-bottom: 0; }

/* The keyboard focus ring - for the PAGE CONTENT only.

   `:not(#shell-head *)` is what keeps it out of the CHROME (2026-10-14, user:
   "I don't want the focus ring"). Why the chrome had to opt out: it moves focus
   by script - opening the drawer focuses its ✕ and closing it focuses the burger
   again (shell.js setDrawer) - and `:focus-visible` MATCHES script-moved focus,
   so the blue ring kept appearing on a plain tap. Everything inside #shell-head
   (topbar, header, nav, mega, drawers, overlay) is ring-free now; the content
   below it keeps the indicator for keyboard users, which is where it is needed.
   Complex selectors inside :not() need Chrome/Edge 88+ or Firefox 84+ - the
   browsers these demo pages target. */
.shell :focus-visible:not(#shell-head *) {
    outline: 2px solid var(--sf-brand);
    outline-offset: 3px;
    border-radius: 6px;
}

/* Belt and braces for the chrome: some chrome buttons still carry Tailwind's own
   `focus-visible:outline*` utilities (the five heading action buttons), which are
   injected AFTER this sheet and would paint a grey ring of their own. This rule
   (1,1,0) outranks them. No radius re-statement is needed here - the 6px leak
   came from the rule above, which no longer matches the chrome. */
#shell-head :focus-visible {
    outline: none;
}

/* Current channel links in shell chrome */
#shell-channel-menu a[aria-current="page"] {
    background: var(--sf-brand-soft);
    color: var(--sf-brand-deep);
    font-weight: 700;
}

a[data-drawer-tile][aria-current="page"],
a[data-drawer-tile][aria-current="page"]:hover {
    background: var(--sf-brand);
    color: #fff;
}

a[data-drawer-tile][aria-current="page"] > span:first-child {
    background: rgba(255, 255, 255, .22);
    color: #fff;
}

/* Responsive overrides */
@media (min-width: 1280px) {
    .shell__grid--with-rail {
        grid-template-columns: minmax(0, 1fr) calc(300px + var(--sf-gutter) + 1px);
        align-items: stretch;
        column-gap: var(--sf-gutter);
    }

    .shell__grid--with-rail .shell__rail {
        order: 0;
        padding-inline-start: var(--sf-gutter);
        border-inline-start: 1px solid var(--sf-line);
    }
}

@media (max-width: 767px) {
    /* Let the sticky header use the document scrollport. #shell-head only wraps
       the site chrome, so its box ends too soon to provide a useful sticky range. */
    #shell-head { display: contents; }

    #shell-head header.is-nav-past {
        border-bottom: 1px solid var(--sf-line);
    }

    .shell__burger { display: inline-flex; }

    /* A phone's header row is tighter than its desktop/tablet self: no vertical
       padding at all and a shorter min-height, so the air above AND below the
       burger / logo / icon row shrinks (10px each, was 15 / 21). The min-height
       has to come down with the padding: with the padding zeroed and the row
       still 66px, `items-center` would just re-centre the content and put the
       top air straight back. Id-scoped because the row's own `max-lg:py-3` and
       `max-md:min-h-[66px]` are Tailwind utilities, injected AFTER this sheet. */
    #shell-head header > .shell__inner {
        min-height: 56px;
        padding-block: 0;
    }
    /* …EXCEPT while the mobile search is open (2026-10-20, user: "when I toggle
       the search input in mobile in index page, the padding of the top header is
       zero, please fix"). The row then holds TWO lines - the logo row and the
       search form - and with `padding-block: 0` the input sat flush against the
       header's edges.
       ⚠ The air goes on the FORM, not on the row: the closed row is ONE line
       stretched to `min-height: 56px`, while an open row's first line collapses
       to its content (~51px) - and `align-items: center` then re-centres the
       logo and the buttons a few pixels HIGHER (user: "the logo and buttons are
       moved upwards a few pixels"). Pinning line 1's items back to 56px, and
       keeping the row's padding at 0, holds them exactly where they were. */
    #shell-head.is-searching header > .shell__inner > :not(#shell-search) {
        min-height: 56px;
    }
    #shell-head.is-searching #shell-search {
        margin-block-end: var(--sf-sp-2);
    }
    /* The DEFAULT is "no nav row on a phone" - the burger/drawer is the phone's
       navigation. nav_render() (shell-head.php) RE-ENABLES the row with
       `max-md:block`; Tailwind's sheet compiles after this one, so the utility
       is what wins. A page that passes RAW $nav markup instead must carry
       `max-md:block` itself, or its row stays hidden right here. */
    .shell__nav { display: none; }
    .shell { font-size: 13px; --sf-gutter: var(--sf-sp-4); }
    .ui-select { font-size: 13px; padding-block: 8px; }
}

@media (hover: none) and (pointer: coarse) {
    .shell .ui-select,
    .shell input[type="text"],
    .shell input[type="email"],
    .shell input[type="password"],
    .shell input[type="search"],
    .shell input[type="tel"],
    .shell input[type="number"],
    .shell input[type="date"],
    .shell textarea {
        font-size: 16px;
    }

    /* This restores the CURRENT tab's underline after a TAP on a touch device
       (the "one highlight at a time" hover rule above drops it). */
    .shell__nav:hover .shell__nav-link[aria-current="page"] {
        color: var(--sf-brand);
        border-block-end-color: var(--sf-brand);
    }
}

@media (prefers-reduced-motion: reduce) {
    .shell *,
    .shell *::before,
    .shell *::after {
        transition-duration: .01ms !important;
        animation-duration: .01ms !important;
    }
}

/* ========================== 登出確認 ==========================
   The account menu's 登出 row is a real POST <form> (so signing out still works
   with JS off); shell.js intercepts the submit and opens this native <dialog>.
   It lives HERE rather than in a page sheet because the account menu renders on
   every channel and shell.css is the only sheet all of them load — `.sf-btn`
   belongs to individual page sheets and must not be assumed.
   The dialog IS the scrim (no ::backdrop rule): in modal mode its box covers the
   viewport, and doubling a backdrop with its own background would darken twice. */
.shell-confirm {
    position: fixed;
    inset: 0;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: rgba(2, 6, 23, .45);
    color: var(--sf-ink);
    display: flex;
    align-items: center;
    justify-content: center;
}
.shell-confirm:not([open]) { display: none; }

.shell-confirm__box {
    box-sizing: border-box;
    width: min(360px, calc(100vw - 32px));
    padding: var(--sf-sp-5);
    border-radius: var(--sf-radius-sm);
    background: var(--sf-surface);
    box-shadow: var(--sf-shadow-lg);
}
.shell-confirm__title {
    margin: 0 0 var(--sf-sp-2);
    font-size: 16px;
    font-weight: 700;
    line-height: 1.4;
    color: var(--sf-ink);
}
.shell-confirm__text {
    margin: 0 0 var(--sf-sp-5);
    font-size: 13px;
    line-height: 1.6;
    color: var(--sf-muted);
}
.shell-confirm__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sf-sp-2);
}
.shell-confirm__btn {
    cursor: pointer;
    padding: 9px 18px;
    border: 1px solid transparent;
    border-radius: 6px;
    font: inherit;
    font-size: 13px;
    font-weight: 600;
    transition: background-color .15s var(--sf-ease);
}
.shell-confirm__btn--ghost {
    border-color: var(--sf-line);
    background: transparent;
    color: var(--sf-ink);
}
.shell-confirm__btn--ghost:hover { background: var(--sf-fill); }
.shell-confirm__btn--danger {
    background: #d33333;
    color: #fff;
}
.shell-confirm__btn--danger:hover { background: #b62a2a; }
.shell-confirm__btn:focus-visible {
    outline: 2px solid var(--sf-brand);
    outline-offset: 2px;
}
