/* ==========================================================================
   Tailwind utility shim for the front-page content (2026-09-16)
   The theme's tailwind.css is built in the theme repo from the theme's own
   Blade/JS sources, so utilities that only content.html / content.en.html
   use never make it into the production build (the local Vite dev server
   generates them on the fly, which is why the gap only shows live).
   Values follow Tailwind v4 defaults. Add here whenever a new utility is
   used in content*.html and the built theme CSS does not carry it.
   ========================================================================== */
.leading-loose { line-height: 2; }
.leading-snug { line-height: 1.375; }
.mt-5 { margin-top: 1.25rem; }
.p-7 { padding: 1.75rem; }
.self-start { align-self: flex-start; }
.tracking-widest { letter-spacing: 0.1em; }
.underline-offset-2 { text-underline-offset: 2px; }
/* 2026-09-22: the rest of the utilities the prod build lacks (the install
   card head row lost its gap-2.5 between icon, label and badge) */
.gap-2\.5 { gap: 0.625rem; }
.mb-3\.5 { margin-bottom: 0.875rem; }
.pr-1\.5 { padding-right: 0.375rem; }
.text-\[0\.85em\] { font-size: 0.85em; }
html.dark .dark\:bg-blue-950 { background-color: oklch(28.2% 0.091 267.935); }
@media (min-width: 40rem) { .sm\:p-7 { padding: 1.75rem; } }
@media (min-width: 48rem) { .md\:p-9 { padding: 2.25rem; } }

/* ==========================================================================
   Dixlase Brand Front Page
   prose 親 (max-w-4xl) の中で動く前提。grid/カードは not-prose で抜けて使用。
   ========================================================================== */

:root {
    --dx-accent: #4D82EE;
    --dx-accent-soft: rgba(77, 130, 238, 0.10);
    --dx-accent-line: rgba(77, 130, 238, 0.35);
    --dx-fg: #1a1d24;
    --dx-fg-muted: #5b6473;
    --dx-bg-soft: #f7f8fb;
    --dx-border: rgba(15, 17, 23, 0.10);
    --dx-card-bg: #ffffff;
    --dx-card-shadow: 0 1px 2px rgba(15, 17, 23, 0.04), 0 8px 24px rgba(15, 17, 23, 0.04);
    --dx-card-shadow-hover: 0 1px 2px rgba(15, 17, 23, 0.06), 0 16px 40px rgba(15, 17, 23, 0.08);
}

.dark {
    --dx-fg: #e7eaf0;
    --dx-fg-muted: #9aa3b2;
    --dx-bg-soft: rgba(255, 255, 255, 0.03);
    --dx-border: rgba(255, 255, 255, 0.10);
    --dx-card-bg: rgba(255, 255, 255, 0.04);
    --dx-card-shadow: 0 1px 0 rgba(255, 255, 255, 0.03);
    --dx-card-shadow-hover: 0 1px 0 rgba(255, 255, 255, 0.05), 0 16px 40px rgba(0, 0, 0, 0.30);
    --dx-accent: #4D82EE;
    --dx-accent-soft: rgba(77, 130, 238, 0.12);
    --dx-accent-line: rgba(77, 130, 238, 0.40);
}

/* ハッシュリンクや scrollIntoView の着地位置を、固定サイトヘッダー(h-14 = 3.5rem)
   + バッファぶん下にオフセットする。
   セクション・FAQ 項目に個別の scroll-margin-top を書く必要がなくなる。 */
html {
    scroll-padding-top: 10rem;
}

/* --------------------------------------------------------------------------
   ページ内ナビゲーション (TOC)
   -------------------------------------------------------------------------- */

.dx-toc {
    position: sticky;
    /* 固定サイトヘッダー (h-14 = 3.5rem) の真下にスタックさせる。
       管理者ログイン中で管理バー (h-12 = 3rem) が表示されている場合は、
       下の body:has(#admin-bar) ルールで合計 6.5rem 下に押し下げる。 */
    top: 3.5rem;
    z-index: 10;
    display: flex;
    gap: 0.125rem;
    flex-wrap: nowrap;
    overflow-x: auto;
    justify-content: flex-start;
    padding: 0.625rem 1rem;
    /* フルブリード: コンテンツコンテナを飛び出してビューポート幅まで広げる。
       margin-left/right の calc(50% - 50vw) で左右をビューポート端まで延伸。 */
    width: 100vw;
    margin: 0 calc(50% - 50vw) 3rem;
    background: color-mix(in srgb, var(--dx-card-bg) 45%, transparent);
    backdrop-filter: saturate(140%) blur(12px);
    -webkit-backdrop-filter: saturate(140%) blur(12px);
    border-bottom: 1px solid var(--dx-border);
    scrollbar-width: none;
    -ms-overflow-style: none;
}

/* 管理バーが表示されているとき、TOC を管理バー + ヘッダーぶん下に押し下げる。
   :has() は Chrome 105+ / Safari 15.4+ / Firefox 121+ で利用可能。
   非対応ブラウザでは TOC がヘッダーぶんだけ下がる挙動 (上のルール) になる。 */
body:has(#admin-bar) .dx-toc {
    top: 6.5rem;
}

.dx-toc::-webkit-scrollbar {
    display: none;
}

@media (min-width: 768px) {
    .dx-toc {
        flex-wrap: wrap;
        overflow-x: visible;
        justify-content: center;
        gap: 0.25rem;
    }
}

.dx-toc a {
    font-size: 0.8125rem;
    color: var(--dx-fg-muted);
    text-decoration: none;
    padding: 0.375rem 0.75rem;
    border-radius: 999px;
    transition: color 160ms ease, background-color 160ms ease;
    white-space: nowrap;
    flex-shrink: 0;
}

.dx-toc a:hover {
    color: var(--dx-fg);
    background: var(--dx-bg-soft);
}

.dx-toc a.is-active {
    color: var(--dx-accent);
    background: var(--dx-accent-soft);
}

/* --------------------------------------------------------------------------
   セクション共通
   -------------------------------------------------------------------------- */

/* フロントページのテーマ (DixlaseOnePage) は content を
   `<section class="front-content"><div class="container mx-auto px-4">
     <div class="max-w-4xl mx-auto prose ...">` で包む。max-w-4xl (56rem)
   が section を絞ってしまうので、page-scoped な style.css でその cap を
   解除し、philosophy 同様コンテナ幅いっぱい (2xl で約 1488px) まで広がる
   ようにする。 */
.front-content > .container > .max-w-4xl {
    max-width: none;
}

/* 同時に container の左右 padding を philosophy と揃える (md+ で 1.5rem)。
   テーマ既定は px-4 (1rem) のままだとセクション content が 2xl で 1504px
   になり、philosophy の 1488px と僅差で揃わないため。 */
@media (min-width: 768px) {
    .front-content > .container {
        padding-left: 1.5rem;
        padding-right: 1.5rem;
    }
}

.dx-section {
    color: var(--dx-fg);
    padding: 5rem 0;
}

/* --------------------------------------------------------------------------
   Dark モード: フロントページの box 背景を philosophy と同じ半透明 white tint
   (`var(--dx-card-bg)` = rgba(255, 255, 255, 0.04)) に揃える。
   テンプレで `dark:bg-gray-800` の solid #1f2937 が指定されている box が
   多いが、それだとページ背景 (#08080c) より明らかに明るい灰色ブロックとして
   浮いて見える。半透明にすることでページ背景にうっすら馴染む philosophy の
   「透明感」表現に揃える。
   light モードでは元の bg-white / bg-gray-100 がそのまま効く (対象外)。
   -------------------------------------------------------------------------- */
.dark .dx-section [class~="dark:bg-gray-800"] {
    background-color: var(--dx-card-bg);
}

.dx-section + .dx-section {
    border-top: 1px solid var(--dx-border);
}

.dx-section__head {
    text-align: center;
    margin-bottom: 4.5rem;
    padding-bottom: 0.5rem;
}

@media (min-width: 768px) {
    .dx-section__head {
        margin-bottom: 5.5rem;
    }
}

.dx-eyebrow {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--dx-accent);
    margin: 0 0 0.75rem;
}

.dx-heading {
    font-size: clamp(1.75rem, 1.2rem + 1.6vw, 2.5rem);
    font-weight: 700;
    line-height: 1.25;
    margin: 0 0 1rem;
    color: var(--dx-fg);
    letter-spacing: -0.01em;
}

.dx-lede {
    font-size: 1.0625rem;
    line-height: 1.7;
    color: var(--dx-fg-muted);
    max-width: 38rem;
    margin: 0 auto;
}

/* --------------------------------------------------------------------------
   標準プラグイン
   4枚を 2x2 レイアウトで表示
   -------------------------------------------------------------------------- */

.dx-plugins-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.25rem;
}

@media (min-width: 640px) {
    .dx-plugins-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* --------------------------------------------------------------------------
   Coming Soon プラグイン一覧
   1 / 2 / 3 カラム。アイテム数が割り切れず最後の行が満たないとき、
   justify-content: center で残りのアイテムが中央寄せになる。
   -------------------------------------------------------------------------- */

.dx-coming-soon-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.75rem;
}

.dx-coming-soon-grid > * {
    flex: 0 0 100%;
}

@media (min-width: 640px) {
    .dx-coming-soon-grid > * {
        flex: 0 0 calc((100% - 0.75rem) / 2);
    }
}

@media (min-width: 768px) {
    .dx-coming-soon-grid > * {
        flex: 0 0 calc((100% - 1.5rem) / 3);
    }
}

.dx-plugin {
    padding: 1.75rem;
    background: var(--dx-card-bg);
    border: 1px solid var(--dx-border);
    border-radius: 0.875rem;
    transition: border-color 220ms ease, transform 220ms ease, box-shadow 220ms ease;
    display: flex;
    flex-direction: column;
}

.dx-plugin:hover {
    transform: translateY(-2px);
    border-color: var(--dx-accent-line);
    box-shadow: var(--dx-card-shadow-hover);
}

.dx-plugin__icon {
    width: 2.5rem;
    height: 2.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 0.625rem;
    background: var(--dx-accent-soft);
    color: var(--dx-accent);
    margin-bottom: 1.25rem;
}

.dx-plugin__icon svg {
    width: 1.5rem;
    height: 1.5rem;
}

.dx-plugin h3 {
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.5;
    color: var(--dx-fg);
    margin: 0 0 0.625rem;
    letter-spacing: -0.005em;
}

.dx-plugin p {
    font-size: 0.875rem;
    line-height: 1.8;
    color: var(--dx-fg-muted);
    margin: 0;
}

/* --------------------------------------------------------------------------
   Install cards — method icon before the eyebrow (2026-09-18)
   GitHub (Font Awesome brand glyph, already loaded by the theme), the
   Dixlase mark (one-liner installer), Docker and Composer (Simple Icons).
   -------------------------------------------------------------------------- */
.dx-install-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    flex: none;
    line-height: 1;
}
.dx-install-card__icon svg,
.dx-install-card__icon img { width: 100%; height: 100%; display: block; }
.dx-install-card__icon--github { font-size: 1.6rem; color: #1f2328; }
html.dark .dx-install-card__icon--github { color: #f0f6fc; }
.dx-install-card__icon--dixlase svg { fill: #0f172a; }
html.dark .dx-install-card__icon--dixlase svg { fill: #ffffff; }
.dx-install-card__icon--docker svg { fill: #2496ed; }
.dx-install-card__icon--composer {
    padding: .15rem;
    border-radius: .375rem;
    background: #fff;
}
html.dark .dx-install-card__icon--composer { box-shadow: 0 0 0 1px var(--dx-border); }
/* The card head row relies on Tailwind's `gap-2.5`, which the built theme
   CSS does not carry, so icon / eyebrow / badges touched each other. On
   phones, where they also wrap, give the row an explicit gap. */
@media (max-width: 767px) {
    .dx-install-card > .flex:first-child { gap: .5rem .75rem; }
}

/* --------------------------------------------------------------------------
   Demo — mockup picture under the section header (2026-09-18)
   Sits directly under the #demo heading, outside the card, with no frame.
   A light and a dark capture; the page mode (html.dark) picks one, the
   hidden one stays lazy.
   -------------------------------------------------------------------------- */
.dx-demo-visual {
    margin: 0 auto 2.5rem;
    max-width: 64rem;
    line-height: 0;
}
.dx-demo-visual__img {
    display: block;
    width: 100%;
    height: auto;
}
.dx-demo-visual__img--dark { display: none; }
html.dark .dx-demo-visual__img--light { display: none; }
html.dark .dx-demo-visual__img--dark { display: block; }
/* Phone layout: a portrait capture (1000x1700) is swapped in through
   <picture><source media="(max-width: 767px)">. */
.dx-demo-visual picture { display: contents; }

/* --------------------------------------------------------------------------
   テーマ
   現状の利用可能テーマ + 今後のラインナップ予告
   -------------------------------------------------------------------------- */

.dx-theme-card {
    background: var(--dx-card-bg);
    border: 1px solid var(--dx-border);
    border-radius: 1rem;
    overflow: hidden;
}

.dx-theme-card__preview {
    margin: 0;
    background: var(--dx-bg-soft);
    line-height: 0;
    border-bottom: 1px solid var(--dx-border);
}

.dx-theme-card__preview img {
    display: block;
    width: 100%;
    height: auto;
}

/* --------------------------------------------------------------------------
   Device mockup — generic desktop / laptop / tablet / phone (2026-09-18)
   Replaces the static theme preview picture in the #themes card. Adapted
   from the operator's device-mockup.css (realistic style only; the
   "minimal" line-drawing variant was not carried over):
   - no manufacturer shapes, logos or notches
   - scales with the card via container-query units (cqw); no JS
   - all four devices stand on one ground line
   - finish follows the site's own dark-mode switch (`html.dark`, not
     prefers-color-scheme): light page → space black, dark page → silver.
     `data-finish="silver"` / `data-finish="space-black"` on .dm-scene
     pin a finish regardless of mode.
   Each .dm-screen carries a light and a dark capture (.dm-shot--light /
   .dm-shot--dark); the page mode picks one. Capture sizes (object-fit:
   cover, anchored top):
     desktop 1920×1080 (16:9) · laptop 1440×900 (16:10)
     tablet   768×1024 (3:4)  · phone   390×845  (9:19.5)
   -------------------------------------------------------------------------- */
.dx-theme-card__preview {
    padding: clamp(1.25rem, 4vw, 2.75rem) clamp(1rem, 3vw, 2rem) clamp(1rem, 2.5vw, 1.75rem);
}

.dm-scene {
    /* tokens: realistic / space black (light-mode default) */
    --dm-bezel: #0b0c0e;
    --dm-bezel-edge: #202226;
    --dm-metal-1: #55585e;
    --dm-metal-2: #36383c;
    --dm-metal-3: #1d1e21;
    --dm-screen-bg: #15171a;
    --dm-shadow: rgba(15, 20, 30, .30);
    --dm-glare: rgba(255, 255, 255, .07);
    /* outer rim around each display; follows the metal tones, but the
       silver set below tones it down so it does not read as a white
       outline on the dark page */
    --dm-rim-1: var(--dm-metal-2);
    --dm-rim-2: var(--dm-metal-3);

    container-type: inline-size;
    position: relative;
    width: 100%;
    max-width: 1200px;
    margin-inline: auto;
    aspect-ratio: 100 / 46;
    line-height: 0;
}
/* silver: explicit, or automatically on the dark page */
.dm-scene[data-finish="silver"],
html.dark .dm-scene:not([data-finish]) {
    --dm-metal-1: #d2d5d9;
    --dm-metal-2: #a8adb3;
    --dm-metal-3: #7c828a;
    --dm-rim-1: #7d838b;
    --dm-rim-2: #4f545b;
    --dm-shadow: rgba(0, 0, 0, .45);
}
.dm-scene[data-finish="space-black"] {
    --dm-metal-1: #55585e;
    --dm-metal-2: #36383c;
    --dm-metal-3: #1d1e21;
    --dm-rim-1: var(--dm-metal-2);
    --dm-rim-2: var(--dm-metal-3);
    --dm-shadow: rgba(15, 20, 30, .30);
}

.dm-scene *,
.dm-scene *::before,
.dm-scene *::after { box-sizing: border-box; }

.dm-device {
    position: absolute;
    bottom: 2.5cqw;                            /* shared ground line */
    margin: 0;
    filter: drop-shadow(0 1.1cqw 1.4cqw var(--dm-shadow));
}

/* screen (shared) */
.dm-screen {
    position: relative;
    overflow: hidden;
    background: var(--dm-screen-bg);
}
/* Each screen holds a light and a dark capture; show the one that
   matches the page mode (html.dark). Hidden images stay lazy. */
.dm-shot--dark { display: none; }
html.dark .dm-shot--light { display: none; }
html.dark .dm-shot--dark { display: block; }
.dm-screen > img,
.dm-screen > picture > img,
.dm-screen > video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top center;
}
.dm-screen::after {                         /* subtle glass glare */
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(115deg, var(--dm-glare) 0%, transparent 55%);
}

/* desktop monitor (all-in-one style stand) */
.dm-desktop { left: 22cqw; width: 56cqw; z-index: 1; }
.dm-desktop__frame {
    position: relative;
    z-index: 1;
    padding: .75cqw .75cqw 1.6cqw;
    border-radius: .9cqw;
    background: var(--dm-bezel);
    box-shadow: inset 0 0 0 .12cqw var(--dm-bezel-edge), 0 0 0 .12cqw var(--dm-rim-2);
}
.dm-desktop .dm-screen { aspect-ratio: 16 / 9; border-radius: .25cqw; }
.dm-desktop__neck {                          /* rectangular plate */
    position: relative;
    width: 9cqw;
    height: 9cqw;
    margin: 0 auto;
    background: linear-gradient(180deg, var(--dm-metal-3) 0, var(--dm-metal-2) 18%, var(--dm-metal-1) 70%, var(--dm-metal-2) 100%);
}
.dm-desktop__neck::before {                  /* shadow under the display */
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 1.2cqw;
    background: linear-gradient(180deg, rgba(0, 0, 0, .35), transparent);
}
.dm-desktop__base {                          /* flat foot */
    width: 15cqw;
    height: .9cqw;
    margin: 0 auto;
    border-radius: .2cqw .2cqw .45cqw .45cqw;
    background: linear-gradient(180deg, var(--dm-metal-1), var(--dm-metal-2) 60%, var(--dm-metal-3));
}

/* laptop */
.dm-laptop { left: 0; width: 42cqw; z-index: 3; }
.dm-laptop__lid {
    position: relative;
    width: 34.7cqw;
    margin: 0 auto;
    padding: 1cqw .73cqw 1cqw;
    border-radius: 1cqw 1cqw .2cqw .2cqw;
    background: var(--dm-bezel);
    box-shadow: inset 0 0 0 .12cqw var(--dm-bezel-edge), 0 0 0 .12cqw var(--dm-rim-2);
}
.dm-laptop__lid::before {                  /* camera */
    content: "";
    position: absolute;
    top: .38cqw;
    left: 50%;
    width: .28cqw;
    height: .28cqw;
    border-radius: 50%;
    transform: translateX(-50%);
    background: #2c3036;
}
.dm-laptop__lid::after {                   /* hinge */
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: .32cqw;
    background: linear-gradient(180deg, #1c1f23, #3a3e44);
}
.dm-laptop .dm-screen { aspect-ratio: 16 / 10; border-radius: .2cqw; }
.dm-laptop__base {
    position: relative;
    height: 1.4cqw;
    border-radius: .25cqw .25cqw 1.3cqw 1.3cqw / .25cqw .25cqw 1.1cqw 1.1cqw;
    background: linear-gradient(180deg, var(--dm-metal-1) 0 30%, var(--dm-metal-2) 70%, var(--dm-metal-3));
}
.dm-laptop__base::before {                 /* top-edge highlight */
    content: "";
    position: absolute;
    left: 1%; right: 1%; top: 0;
    height: .12cqw;
    background: rgba(255, 255, 255, .55);
}

/* tablet */
.dm-tablet { right: 9cqw; width: 18cqw; z-index: 2; }
.dm-tablet__frame {
    position: relative;
    padding: .6cqw;
    border-radius: 1.35cqw;
    background: var(--dm-bezel);
    box-shadow: 0 0 0 .12cqw var(--dm-rim-1), 0 0 0 .18cqw var(--dm-rim-2);
}
.dm-tablet .dm-screen { aspect-ratio: 3 / 4; border-radius: .8cqw; }

/* phone */
.dm-phone { right: 2cqw; width: 9cqw; z-index: 4; }
.dm-phone__frame {
    position: relative;
    padding: .3cqw;
    border-radius: 1.35cqw;
    background: var(--dm-bezel);
    box-shadow: 0 0 0 .1cqw var(--dm-rim-1), 0 0 0 .16cqw var(--dm-rim-2);
}
.dm-phone__frame::before {                 /* side button */
    content: "";
    position: absolute;
    right: -.32cqw;
    top: 22%;
    width: .14cqw;
    height: 2cqw;
    border-radius: .1cqw;
    background: var(--dm-rim-1);
}
.dm-phone .dm-screen { aspect-ratio: 9 / 19.5; border-radius: 1.05cqw; }
.dm-phone .dm-screen::before {             /* punch-hole camera */
    content: "";
    position: absolute;
    z-index: 1;
    top: .5cqw;
    left: 50%;
    width: .55cqw;
    height: .55cqw;
    border-radius: 50%;
    transform: translateX(-50%);
    background: #050607;
}

/* ground contact shadows */
.dm-laptop::after,
.dm-tablet::after,
.dm-phone::after,
.dm-desktop::after {
    content: "";
    position: absolute;
    z-index: -1;
    left: 4%; right: 4%;
    bottom: -1.2cqw;
    height: 1.6cqw;
    border-radius: 50%;
    background: radial-gradient(closest-side, var(--dm-shadow), transparent);
}
.dm-desktop::after { left: 34%; right: 34%; bottom: -.9cqw; }

/* Phone layout (2026-09-19): the side-by-side row gets too small under
   768px, so the scene switches to a stacked composition — the monitor
   on top, laptop / tablet / phone in a row below it. Same markup and
   captures; only the scene aspect and device placement change. */
@media (max-width: 767px) {
    .dm-scene { aspect-ratio: 100 / 112; max-width: 640px; }
    .dm-scene .dm-device { transform-origin: left bottom; }
    .dm-scene .dm-desktop { left: 5.2cqw; top: 1.5cqw; bottom: auto; transform: scale(1.6); transform-origin: left top; }
    .dm-scene .dm-laptop { left: 0; bottom: 3cqw; transform: scale(1.5); z-index: 3; }
    .dm-scene .dm-tablet { right: auto; left: 58.5cqw; bottom: 3cqw; transform: scale(1.6); z-index: 4; }
    .dm-scene .dm-phone  { right: auto; left: 84.3cqw; bottom: 3cqw; transform: scale(1.6); z-index: 5; }
}

.dx-theme-card__info {
    padding: 1.75rem;
}

@media (min-width: 768px) {
    .dx-theme-card__info {
        padding: 2.25rem 2.5rem;
    }
}

/* Theme card: the theme's own artwork sits beside the description. The device
   mockup above shows what the theme renders; this shows the theme's identity —
   the same card the admin's theme list uses, so the two places match. Stacked
   below 768px, where two columns would squeeze the description too narrow. */
.dx-theme-card__thumb {
    margin: 0 0 1.5rem;
}

.dx-theme-card__thumb img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 0.75rem;
    border: 1px solid var(--dx-border);
}

@media (min-width: 768px) {
    .dx-theme-card__info {
        display: flex;
        align-items: center;
        gap: 2.25rem;
    }

    .dx-theme-card__thumb {
        flex: 0 0 17.5rem;
        margin: 0;
    }

    .dx-theme-card__text {
        flex: 1 1 auto;
        min-width: 0;
    }
}

.dx-theme-card__tag {
    display: inline-block;
    padding: 0.25rem 0.625rem;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    color: var(--dx-accent);
    background: var(--dx-accent-soft);
    border: 1px solid var(--dx-accent-line);
    border-radius: 999px;
    margin: 0 0 0.875rem;
}

.dx-theme-card h3 {
    font-size: 1.25rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--dx-fg);
    margin: 0 0 0.875rem;
    letter-spacing: -0.005em;
}

.dx-theme-card p {
    font-size: 0.9375rem;
    line-height: 1.85;
    color: var(--dx-fg-muted);
    margin: 0;
}

.dx-theme-coming {
    margin-top: 1.5rem;
    margin-left: auto;
    margin-right: auto;
    padding: 1rem 1.75rem;
    /* 内容に合わせて shrink-to-fit (テキスト 1 行ぶんの最小幅で中央寄せ) */
    width: fit-content;
    max-width: 100%;
    text-align: center;
    font-size: 0.875rem;
    line-height: 1.7;
    color: var(--dx-fg-muted);
    background: var(--dx-bg-soft);
    border: 1px dashed var(--dx-border);
    border-radius: 0.75rem;
}

/* --------------------------------------------------------------------------
   セクション末尾の補足説明ボックス / 中央寄せで幅を抑えるボックス
   フルブリードしたセクション全幅まで広がると間延びするので、
   読みやすい幅 (cookie バナーぐらい) で中央寄せ。
   - .dx-section-note: 各セクション末尾の注記 (Install / Requirements など)
   - #demo > article: Demo セクションの単体カード
   - .dx-plugins-note: Plugins セクションの「管理画面から直接 DL...」注記
   - .dx-plugins-future: Plugins セクションの「Coming Soon」ブロック全体
   -------------------------------------------------------------------------- */
.dx-section-note,
#demo > article,
.dx-plugins-note,
.dx-plugins-future,
#requirements > .grid {
    max-width: 60rem;
    margin-left: auto;
    margin-right: auto;
}

/* --------------------------------------------------------------------------
   CTA Buttons
   -------------------------------------------------------------------------- */

.dx-cta-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 2rem;
}

.dx-cta-row--center {
    justify-content: center;
    margin-top: 2.5rem;
}

.dx-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 1.5rem;
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--dx-fg);
    background: var(--dx-card-bg);
    border: 1px solid var(--dx-border);
    border-radius: 0.625rem;
    text-decoration: none;
    transition: transform 160ms ease, border-color 160ms ease, background-color 160ms ease;
}

.dx-btn:hover {
    transform: translateY(-1px);
    border-color: var(--dx-accent-line);
    background: var(--dx-bg-soft);
}

.dx-btn--primary {
    color: #ffffff;
    background: var(--dx-accent);
    border-color: var(--dx-accent);
}

.dx-btn--primary:hover {
    background: color-mix(in srgb, var(--dx-accent) 88%, #000);
    border-color: color-mix(in srgb, var(--dx-accent) 88%, #000);
}

.dx-btn--disabled {
    opacity: 0.55;
    cursor: not-allowed;
    pointer-events: none;
}

.dx-btn__badge {
    display: inline-flex;
    align-items: center;
    margin-left: 0.5rem;
    padding: 0.0625rem 0.5rem;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    color: #b45309;
    background: rgba(251, 191, 36, 0.14);
    border: 1px solid rgba(251, 191, 36, 0.45);
    border-radius: 999px;
}

.dark .dx-btn__badge {
    color: #fbbf24;
    background: rgba(251, 191, 36, 0.10);
    border-color: rgba(251, 191, 36, 0.35);
}

/* --------------------------------------------------------------------------
   インストールセクション ── Status badge & Copy button
   -------------------------------------------------------------------------- */

.dx-status-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.0625rem 0.5rem;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.625rem;
    font-weight: 600;
    letter-spacing: 0.12em;
    border-radius: 999px;
}

.dx-status-badge--coming {
    color: #b45309;
    background: rgba(251, 191, 36, 0.14);
    border: 1px solid rgba(251, 191, 36, 0.45);
}

.dark .dx-status-badge--coming {
    color: #fbbf24;
    background: rgba(251, 191, 36, 0.10);
    border-color: rgba(251, 191, 36, 0.35);
}

/* Code block + copy button (1つの統一ボックス内に並ぶ) */

.dx-copy-btn {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 0.375rem;
    color: var(--dx-fg-muted);
    cursor: pointer;
    transition: background-color 160ms ease, color 160ms ease;
}

.dx-copy-btn:hover {
    background: color-mix(in srgb, var(--dx-fg) 10%, transparent);
    color: var(--dx-fg);
}

.dx-copy-btn:focus-visible {
    outline: 2px solid var(--dx-accent);
    outline-offset: 2px;
}

.dx-copy-btn svg {
    width: 1rem;
    height: 1rem;
}

.dx-copy-btn__icon-default {
    display: inline-block;
}

.dx-copy-btn__icon-success {
    display: none;
}

.dx-copy-btn.is-copied {
    background: rgba(34, 197, 94, 0.18);
    color: #4ade80;
}

.dx-copy-btn.is-copied .dx-copy-btn__icon-default {
    display: none;
}

.dx-copy-btn.is-copied .dx-copy-btn__icon-success {
    display: inline-block;
}

/* --------------------------------------------------------------------------
   About
   -------------------------------------------------------------------------- */

/* #overview figure — the three-layer security diagram (2026-09-13).
   The <figure> had no rules of its own: preflight zeroes its margin,
   so the image sat flush against .dx-about below, while the shared
   .dx-section__head margin (4.5rem) plus the SVG's own top padding
   opened an over-wide gap above it. Tighten above, open below, and
   cap the width on desktop — the 1420px-wide asset at the full
   section width rendered its labels oversized. Phones are unaffected
   by the cap (the container is narrower than it). */
#overview .dx-section__head {
    margin-bottom: 2rem;
}
.dx-overview-figure {
    max-width: 54rem;
    margin: 0 auto 3rem;
}

/* --------------------------------------------------------------------------
   Overview — core defense diagram (.dx-shield, 2026-09-18)
   HTML/CSS rebuild of the former 3-layer SVG artwork (operator's
   dixlase-shield prototype). Outside list / pentagon core / inside list,
   one column on phones and three from 720px. Light is the default; the
   site's dark switch (html.dark) swaps the tokens. The list entrance
   keys off `.is-visible`, which the existing [data-dx-reveal] observer in
   script.js adds, so no extra JS is needed; the core glow breathes.
   -------------------------------------------------------------------------- */
.dx-shield {
    --acc: #4d82ee; --grn: #34d399; --vio: #a78bfa;
    --txt: #16202f; --faint: #7d8798; --line: rgba(47, 107, 214, .22);
    --ring-tx: #2f6bd6; --sec: #2f6bd6;
    --bloom-a: rgba(54, 115, 238, .40); --bloom-b: rgba(54, 115, 238, 0);
    --disc-fg: #0f172a; --disc-mark: #ffffff;
    font-family: 'Noto Sans JP', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic', sans-serif;
    color: var(--txt);
    max-width: 480px;
    margin: 0 auto 3rem;
    padding: 1.5rem 0 1.25rem;
    display: grid;
    gap: 22px;
}
html.dark .dx-shield {
    --txt: #dbe3f0; --faint: #7c8296; --line: rgba(157, 192, 251, .18);
    --ring-tx: #9dc0fb; --sec: #9dc0fb;
    --bloom-a: rgba(58, 123, 255, .55);
    --disc-fg: #ffffff; --disc-mark: #ffffff;
}

/* section heading + minimal list */
.dx-shield__head { font-size: 13px; font-weight: 700; letter-spacing: .3em; margin: 0 0 10px; }
.dx-shield__head--out  { color: var(--grn); }
.dx-shield__head--in   { color: var(--vio); }
.dx-shield__head--core { color: var(--sec); }
.dx-shield__list { list-style: none; margin: 0; padding: 0; }
.dx-shield__list li {
    position: relative;
    padding: 9px 0 9px 20px;
    font-size: 17px;
    font-weight: 500;
    line-height: 1.3;
}
.dx-shield__list li + li { border-top: 1px solid var(--line); }
.dx-shield__list li::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 50%;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    transform: translateY(-50%);
    background: var(--dot);
}
.dx-shield__list--out { --dot: var(--grn); }
.dx-shield__list--in  { --dot: var(--vio); }

/* core block: pentagon + ring labels */
.dx-shield__core {
    display: grid;
    grid-template-columns: auto auto;
    justify-content: center;
    column-gap: 16px;
    align-items: start;
}
.dx-shield__core .dx-shield__head { grid-column: 1 / -1; }
.dx-shield__pent {
    position: relative;
    width: min(248px, 52vw);
    aspect-ratio: 1;
    justify-self: center;
    align-self: center;
}
/* nebula — two drifting blobs so the glow breathes */
.dx-shield__pent::before,
.dx-shield__pent::after {
    content: "";
    position: absolute;
    inset: -55%;
    border-radius: 50%;
    pointer-events: none;
    will-change: transform, opacity;
}
.dx-shield__pent::before {
    background: radial-gradient(circle, var(--bloom-a) 0%, rgba(58, 123, 255, .16) 32%, var(--bloom-b) 66%);
    animation: dx-shield-breathe-a 5s ease-in-out infinite alternate;
}
.dx-shield__pent::after {
    inset: -35%;
    background: radial-gradient(circle, rgba(82, 139, 255, .32) 0%, rgba(58, 123, 255, .10) 40%, var(--bloom-b) 70%);
    animation: dx-shield-breathe-b 7s ease-in-out infinite alternate;
}
@keyframes dx-shield-breathe-a {
    0%   { transform: translate(-10%, -8%) scale(.80); opacity: .55; }
    50%  { transform: translate(12%, 9%) scale(1.30);  opacity: 1; }
    100% { transform: translate(-8%, 12%) scale(1.06); opacity: .75; }
}
@keyframes dx-shield-breathe-b {
    0%   { transform: translate(10%, 7%) scale(1.18) rotate(0deg);    opacity: .45; }
    50%  { transform: translate(-13%, -10%) scale(.78) rotate(20deg); opacity: 1; }
    100% { transform: translate(8%, -12%) scale(1.34) rotate(-15deg); opacity: .6; }
}

/* five pentagon layers (inline SVG: strokes / dashes) */
.dx-shield__pent > svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.dx-shield__pent .ring     { fill: none; stroke: var(--acc); stroke-opacity: .22; stroke-width: 1; stroke-dasharray: 3 6; stroke-linejoin: round; }
.dx-shield__pent .outer    { fill: var(--acc); fill-opacity: .05; stroke: var(--acc); stroke-opacity: .55; stroke-width: 1.6; stroke-linejoin: round; }
.dx-shield__pent .edge-out { fill: none; stroke: var(--grn); stroke-opacity: .6; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; }
.dx-shield__pent .edge-in  { fill: none; stroke: var(--vio); stroke-opacity: .6; stroke-width: 5; stroke-linecap: round; }

/* core wordmark */
.dx-shield__disc {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: grid;
    place-items: center;
    gap: 2px;
    color: var(--disc-fg);
    text-align: center;
}
.dx-shield__disc svg { width: 30px; height: 30px; fill: var(--disc-mark); filter: drop-shadow(0 0 6px rgba(47, 107, 214, .55)); }
.dx-shield__disc b { font: 500 16px/1 'Cormorant Garamond', Garamond, serif; letter-spacing: .22em; }
html.dark .dx-shield__disc svg { filter: drop-shadow(0 0 10px rgba(160, 200, 255, .7)); }
html.dark .dx-shield__disc b { text-shadow: 0 0 12px rgba(160, 200, 255, .6); }

/* ring labels */
.dx-shield__rings { list-style: none; margin: 0; padding: 0; align-self: center; display: grid; gap: 10px; }
.dx-shield__rings li {
    position: relative;
    padding-left: 14px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ring-tx);
    white-space: nowrap;
}
.dx-shield__rings li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 8px;
    height: 1px;
    background: var(--acc);
    opacity: .5;
}

.dx-shield__cap { margin: 0; text-align: center; font-size: 13px; color: var(--faint); }

/* The entrance below slides items in from ±28px, and on a viewport just
   wide enough to fit the section those un-revealed items stick out to the
   right and give the page a horizontal scrollbar until the reader scrolls
   far enough to trigger the reveal (2026-09-24). Clip horizontally — `clip`
   rather than `hidden`, so no scroll container is created and sticky
   elements keep working. */
.dx-shield {
    overflow-x: clip;
}

/* entrance: lists slide in from their side, staggered; ring labels follow */
.dx-shield__list li {
    opacity: 0;
    transform: translateX(var(--from, -28px));
    transition: opacity .45s ease, transform .55s cubic-bezier(.2, .7, .2, 1);
    transition-delay: calc(var(--i) * 70ms);
}
.dx-shield__list--out li { --from: -28px; }
.dx-shield__list--in  li { --from: 28px; }
.dx-shield.is-visible .dx-shield__list li { opacity: 1; transform: none; }
.dx-shield__rings li {
    opacity: 0;
    transform: translateX(14px);
    transition: opacity .4s ease, transform .5s ease;
    transition-delay: calc(350ms + var(--i) * 60ms);
}
.dx-shield.is-visible .dx-shield__rings li { opacity: 1; transform: none; }

@media (max-width: 719px) {
    /* Headings and the outside / inside lists centred; the pentagon sits
       on the page's centre line and the ring labels stay at the right
       edge of the same row (a slight overlap with the pentagon is fine). */
    .dx-shield__head { text-align: center; }
    .dx-shield__list li { padding-left: 0; text-align: center; }
    .dx-shield__list li::before {
        position: static;
        display: inline-block;
        margin-right: 10px;
        vertical-align: .12em;
        transform: none;
    }
    .dx-shield__core { grid-template-columns: 1fr; }
    .dx-shield__pent,
    .dx-shield__rings { grid-row: 2; grid-column: 1; }
    .dx-shield__pent { justify-self: center; }
    .dx-shield__rings { justify-self: end; position: relative; z-index: 1; }

    /* same full-bleed clip as .dx-eco: the pentagon's glow reaches the
       screen edges without widening the page */
    .dx-shield {
        max-width: none;
        margin-inline: calc(50% - 50vw);
        padding-inline: max(calc(50vw - 50%), calc((100vw - 480px) / 2));
        overflow-x: hidden;
        overflow-x: clip;
    }
}
@media (max-width: 400px) {
    .dx-shield__list li { font-size: 15px; }
    .dx-shield__rings li { font-size: 12px; }
}
@media (min-width: 720px) {   /* tablet+: three columns, pentagon in the middle */
    .dx-shield {
        max-width: 1100px;
        grid-template-columns: 1fr auto 1fr;
        align-items: center;
        column-gap: 40px;
    }
    .dx-shield__core { grid-template-columns: auto; row-gap: 14px; justify-items: center; }
    .dx-shield__pent { width: 248px; }
    .dx-shield__cap { grid-column: 1 / -1; }
}
@media (prefers-reduced-motion: reduce) {
    .dx-shield__pent::before,
    .dx-shield__pent::after { animation: none; }
    .dx-shield__list li,
    .dx-shield__rings li { opacity: 1; transform: none; transition: none; }
}

.dx-about {
    max-width: 42rem;
    margin: 0 auto;
}

.dx-about p {
    font-size: 1rem;
    line-height: 1.9;
    color: var(--dx-fg);
    margin: 0 0 1.25rem;
}

.dx-about__tagline {
    margin-top: 2rem !important;
    padding-top: 1.5rem;
    border-top: 1px solid var(--dx-border);
    font-weight: 500;
    color: var(--dx-fg);
}

.dx-about__tagline span {
    display: inline-block;
    margin-left: 0.5rem;
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.875rem;
    color: var(--dx-fg-muted);
    letter-spacing: 0.02em;
}

/* --------------------------------------------------------------------------
   セクション説明用プレースホルダー画像
   実画像が用意できたら img の src を差し替え/外部ファイル化する想定
   -------------------------------------------------------------------------- */

.dx-placeholder {
    margin: 2.5rem auto;
    border-radius: 0.875rem;
    overflow: hidden;
    background: var(--dx-bg-soft);
    border: 1px solid var(--dx-border);
    line-height: 0;
}

.dx-placeholder img {
    display: block;
    width: 100%;
    height: auto;
}

.dx-placeholder--full { max-width: 100%; }
.dx-placeholder--lg   { max-width: 720px; }
.dx-placeholder--md   { max-width: 560px; }
.dx-placeholder--sm   { max-width: 420px; }
.dx-placeholder--xs   { max-width: 340px; }

/* --------------------------------------------------------------------------
   Reveal アニメーション
   -------------------------------------------------------------------------- */

[data-dx-reveal] {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 600ms ease, transform 600ms ease;
}

[data-dx-reveal].is-visible {
    opacity: 1;
    transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
    [data-dx-reveal] {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .dx-card,
    .dx-pillar,
    .dx-btn {
        transition: none;
    }
}

/* --------------------------------------------------------------------------
   Overview コンセプト図 (HTML/CSS)
   左カラム = プラグイン、中央 = Core、右カラム = テーマ & DevKit。
   全テキストはマークアップで持つため JA/EN そのまま使え、ズームに耐える。
   -------------------------------------------------------------------------- */

.dx-overview-diagram {
    /* フルブリード: 親 (max-w-4xl 等の prose コンテナ) を抜けて
       ビューポート端まで背景を伸ばす。内側の grid は
       max-width で抑えて読みやすい幅に保つ。 */
    width: 100vw;
    margin: 0 calc(50% - 50vw) 3rem;
    padding: 2.5rem 1.5rem 2rem;
    border-top: 1px solid var(--dx-border);
    border-bottom: 1px solid var(--dx-border);
    background: var(--dx-bg-soft);
}

@media (min-width: 768px) {
    .dx-overview-diagram {
        padding: 3rem 2.5rem 2.5rem;
    }
}

.dx-overview-diagram__grid {
    max-width: 72rem;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr;
    gap: 1.5rem;
    align-items: center;
}

@media (min-width: 768px) {
    .dx-overview-diagram__grid {
        grid-template-columns: 1fr 1.3fr 1fr;
        gap: 2.5rem;
    }
}

.dx-overview-diagram__col-header {
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--dx-accent);
    margin: 0 0 0.875rem;
    text-align: center;
}

.dx-overview-diagram__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.dx-overview-diagram__pill {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.75rem 1rem;
    background: var(--dx-card-bg);
    border: 1px solid var(--dx-border);
    border-radius: 0.5rem;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--dx-fg);
    line-height: 1.4;
}

.dx-overview-diagram__pill::before {
    content: "";
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 0.1875rem;
    background: var(--dx-accent);
    flex-shrink: 0;
}

.dx-overview-diagram__core {
    padding: 1.5rem 1.25rem;
    background: var(--dx-card-bg);
    border: 1px solid var(--dx-accent-line);
    border-radius: 0.875rem;
    text-align: center;
    box-shadow: 0 0 32px var(--dx-accent-soft), 0 1px 2px rgba(15, 17, 23, 0.05);
}

.dx-overview-diagram__core-mark {
    display: inline-block;
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    background: linear-gradient(135deg, var(--dx-accent), color-mix(in srgb, var(--dx-accent) 60%, white));
    margin: 0 0 0.625rem;
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--dx-accent) 40%, transparent), 0 4px 14px var(--dx-accent-soft);
}

.dx-overview-diagram__core h3 {
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--dx-fg);
    margin: 0 0 0.25rem;
    letter-spacing: -0.005em;
}

.dx-overview-diagram__core-sub {
    font-size: 0.8125rem;
    color: var(--dx-fg-muted);
    margin: 0 0 0.875rem;
}

.dx-overview-diagram__core-tags {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.375rem;
}

.dx-overview-diagram__core-tags span {
    display: inline-block;
    padding: 0.1875rem 0.625rem;
    font-size: 0.6875rem;
    font-weight: 600;
    color: var(--dx-accent);
    background: var(--dx-accent-soft);
    border: 1px solid var(--dx-accent-line);
    border-radius: 999px;
    letter-spacing: 0.01em;
}

.dx-overview-diagram__tagline {
    max-width: 72rem;
    margin: 1.75rem auto 0;
    text-align: center;
    font-size: 0.8125rem;
    color: var(--dx-fg-muted);
    line-height: 1.6;
}

/* --------------------------------------------------------------------------
   Plugins セクション — 管理画面のプラグイン一覧 (カード型) モックアップ
   実際の管理画面の plugin gallery を模した見た目。light / dark 両モードに
   対応するため、各色は --dxpm-* CSS 変数で管理し、`.dark` のときだけ上書き
   する。 light モードはホワイト寄りの admin、dark モードは従来通り。
   -------------------------------------------------------------------------- */

.dx-plugins-mockup {
    /* 外枠 (旧フルブリード暗色パネル) は撤去。admin chrome が
       コンテンツ幅内に自然に収まる形にする。 */
    margin: 0 0 3rem;

    /* light mode の admin look (ホワイト + slate アクセント) */
    --dxpm-chrome-bg: #ffffff;
    --dxpm-chrome-border: rgba(15, 17, 23, 0.10);
    --dxpm-topbar-bg: #f1f5f9;          /* slate-100 */
    --dxpm-topbar-border: rgba(15, 17, 23, 0.08);
    --dxpm-topbar-text: #0f172a;        /* slate-900 */
    --dxpm-utility-icon-bg: rgba(15, 17, 23, 0.15);
    --dxpm-sidebar-bg: #f8fafc;         /* slate-50 */
    --dxpm-sidebar-border: rgba(15, 17, 23, 0.08);
    --dxpm-nav-text: #64748b;           /* slate-500 */
    --dxpm-nav-mark: rgba(15, 17, 23, 0.15);
    --dxpm-nav-active-text: #0f172a;
    --dxpm-nav-active-bg: rgba(77, 130, 238, 0.12);
    --dxpm-main-title: #0f172a;
    --dxpm-main-sub: #64748b;
    --dxpm-section-title: #475569;      /* slate-600 */
    --dxpm-card-bg: #f8fafc;            /* slate-50 */
    --dxpm-card-border: rgba(15, 17, 23, 0.10);
    --dxpm-card-name: #0f172a;
    --dxpm-card-codename: #94a3b8;      /* slate-400 */
    --dxpm-card-desc: #64748b;
    --dxpm-thumb-bg: #e2e8f0;
    --dxpm-status-off-text: #64748b;
    --dxpm-status-off-bg: rgba(15, 17, 23, 0.05);
    --dxpm-status-off-border: rgba(15, 17, 23, 0.15);
}

.dark .dx-plugins-mockup {
    /* dark mode (元の admin パネル look) */
    --dxpm-chrome-bg: #0e1018;
    --dxpm-chrome-border: rgba(255, 255, 255, 0.08);
    --dxpm-topbar-bg: #11283f;
    --dxpm-topbar-border: rgba(255, 255, 255, 0.05);
    --dxpm-topbar-text: #ffffff;
    --dxpm-utility-icon-bg: rgba(255, 255, 255, 0.18);
    --dxpm-sidebar-bg: #131521;
    --dxpm-sidebar-border: rgba(255, 255, 255, 0.05);
    --dxpm-nav-text: rgba(255, 255, 255, 0.45);
    --dxpm-nav-mark: rgba(255, 255, 255, 0.15);
    --dxpm-nav-active-text: #ffffff;
    --dxpm-nav-active-bg: rgba(77, 130, 238, 0.18);
    --dxpm-main-title: #ffffff;
    --dxpm-main-sub: rgba(255, 255, 255, 0.5);
    --dxpm-section-title: rgba(255, 255, 255, 0.7);
    --dxpm-card-bg: #1e2130;
    --dxpm-card-border: rgba(255, 255, 255, 0.06);
    --dxpm-card-name: #ffffff;
    --dxpm-card-codename: rgba(255, 255, 255, 0.45);
    --dxpm-card-desc: rgba(255, 255, 255, 0.55);
    --dxpm-thumb-bg: #1a1d2e;
    --dxpm-status-off-text: rgba(255, 255, 255, 0.55);
    --dxpm-status-off-bg: rgba(255, 255, 255, 0.05);
    --dxpm-status-off-border: rgba(255, 255, 255, 0.15);
}

.dx-plugins-mockup__chrome {
    /* セクション幅いっぱい (上書きで 72rem cap を外す) */
    margin: 0 auto;
    background: var(--dxpm-chrome-bg);
    border: 1px solid var(--dxpm-chrome-border);
    border-radius: 0.625rem;
    overflow: hidden;
}

/* ---- Top bar (admin top bar は light / dark とも暗色基調を維持) ---- */
.dx-plugins-mockup__topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.625rem 1rem;
    background: var(--dxpm-topbar-bg);
    border-bottom: 1px solid var(--dxpm-topbar-border);
}

.dx-plugins-mockup__topbar-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--dxpm-topbar-text);
    font-weight: 700;
    font-size: 0.875rem;
}

.dx-plugins-mockup__topbar-brand::before {
    content: "";
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 0.25rem;
    background: var(--dx-accent);
}

.dx-plugins-mockup__topbar-actions {
    display: flex;
    gap: 0.5rem;
}

.dx-plugins-mockup__topbar-actions span {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    background: var(--dxpm-utility-icon-bg);
    border-radius: 0.25rem;
}

.dx-plugins-mockup__topbar-actions span:last-child {
    border-radius: 50%;
}

/* ---- Layout: sidebar + main ----
   Mobile (< 640px): single column (sidebar hidden)
   sm+ (≥ 640px): 2 columns (sidebar | main), like the actual admin PC.
   NOTE: this wrapper is intentionally `__layout` not `__body` because
   the card's inner text area below also uses `__body`, and the two
   would collide if they shared the same class. */
.dx-plugins-mockup__layout {
    display: grid;
    grid-template-columns: 1fr;
}

@media (min-width: 640px) {
    .dx-plugins-mockup__layout {
        grid-template-columns: 11rem 1fr;
    }
}

@media (min-width: 768px) {
    .dx-plugins-mockup__layout {
        grid-template-columns: 13rem 1fr;
    }
}

/* Sidebar: hidden on mobile, visible at sm+ */
.dx-plugins-mockup__sidebar {
    display: none;
    background: var(--dxpm-sidebar-bg);
    padding: 0.875rem 0.5rem;
    border-right: 1px solid var(--dxpm-sidebar-border);
}

@media (min-width: 640px) {
    .dx-plugins-mockup__sidebar {
        display: block;
    }
}

@media (min-width: 768px) {
    .dx-plugins-mockup__sidebar {
        padding: 1rem 0.75rem;
    }
}

.dx-plugins-mockup__nav {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
}

.dx-plugins-mockup__nav li {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4375rem 0.5rem;
    border-radius: 0.375rem;
    color: var(--dxpm-nav-text);
    font-size: 0.6875rem;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@media (min-width: 768px) {
    .dx-plugins-mockup__nav li {
        gap: 0.625rem;
        padding: 0.5rem 0.625rem;
        font-size: 0.75rem;
    }
}

.dx-plugins-mockup__nav li::before {
    content: "";
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 0.1875rem;
    background: var(--dxpm-nav-mark);
    flex-shrink: 0;
}

@media (min-width: 768px) {
    .dx-plugins-mockup__nav li::before {
        width: 0.75rem;
        height: 0.75rem;
    }
}

.dx-plugins-mockup__nav li.is-active {
    color: var(--dxpm-nav-active-text);
    background: var(--dxpm-nav-active-bg);
}

.dx-plugins-mockup__nav li.is-active::before {
    background: var(--dx-accent);
}

/* Main column */
.dx-plugins-mockup__main {
    padding: 1.5rem 1.5rem 1.75rem;
}

@media (min-width: 768px) {
    .dx-plugins-mockup__main {
        padding: 2rem 2.25rem 2.25rem;
    }
}

.dx-plugins-mockup__main-title {
    margin: 0 0 0.375rem;
    color: var(--dxpm-main-title);
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

.dx-plugins-mockup__main-sub {
    margin: 0 0 1.75rem;
    color: var(--dxpm-main-sub);
    font-size: 0.8125rem;
}

.dx-plugins-mockup__section-title {
    margin: 0 0 1rem;
    color: var(--dxpm-section-title);
    font-size: 0.8125rem;
    font-weight: 600;
}

/* ---- Card grid: 1 / 2 / 3 cols. flex-wrap + justify-content: center で
   アイテム数が割り切れない場合も最終行が中央寄せに (6 plugins → 3 + 3)。 */
.dx-plugins-mockup__grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 1.25rem;
}

.dx-plugins-mockup__grid > * {
    flex: 0 0 100%;
}

@media (min-width: 640px) {
    .dx-plugins-mockup__grid > * {
        flex: 0 0 calc((100% - 1.25rem) / 2);
    }
}

@media (min-width: 768px) {
    .dx-plugins-mockup__grid {
        gap: 1.5rem;
    }
    .dx-plugins-mockup__grid > * {
        flex: 0 0 calc((100% - 3rem) / 3);
    }
}

.dx-plugins-mockup__card {
    display: flex;
    flex-direction: column;
    background: var(--dxpm-card-bg);
    border: 1px solid var(--dxpm-card-border);
    border-radius: 0.625rem;
    overflow: hidden;
    color: var(--dxpm-card-name);
}

.dx-plugins-mockup__thumb {
    aspect-ratio: 2 / 1;
    overflow: hidden;
    background: var(--dxpm-thumb-bg);
}

.dx-plugins-mockup__thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.dx-plugins-mockup__body {
    padding: 1.125rem 1.25rem 1.25rem;
    display: flex;
    flex-direction: column;
    flex: 1;
}

.dx-plugins-mockup__name {
    color: var(--dxpm-card-name);
    font-size: 0.9375rem;
    font-weight: 700;
    margin: 0 0 0.125rem;
}

.dx-plugins-mockup__codename {
    margin: 0 0 0.625rem;
    color: var(--dxpm-card-codename);
    font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
    font-size: 0.6875rem;
}

.dx-plugins-mockup__status {
    align-self: flex-start;
    padding: 0.1875rem 0.5rem;
    border-radius: 999px;
    font-size: 0.625rem;
    font-weight: 700;
    margin-bottom: 0.625rem;
}

.dx-plugins-mockup__status--on {
    color: #4ade80;
    background: rgba(74, 222, 128, 0.14);
    border: 1px solid rgba(74, 222, 128, 0.3);
}

.dx-plugins-mockup__status--off {
    color: var(--dxpm-status-off-text);
    background: var(--dxpm-status-off-bg);
    border: 1px solid var(--dxpm-status-off-border);
}

.dx-plugins-mockup__desc {
    margin: 0;
    color: var(--dxpm-card-desc);
    font-size: 0.75rem;
    line-height: 1.65;
}

/* --------------------------------------------------------------------------
   Ecosystem diagram (.dx-eco, 2026-09-18)
   HTML/CSS rebuild of the former #ecosystem SVG pictures (operator's
   dixlase-ecosystem_2 prototype — minimal rows, open core frame): plugin
   rows on the left, the core in the
   middle, theme / DevKit chips on the right, joined by connector curves
   drawn into .dx-eco__wires by script.js (measured with offsetLeft/Top,
   so the chips' entrance transform does not skew them; redrawn on
   resize and after fonts load). Light is the default; html.dark swaps
   the tokens. The entrance keys off `.is-visible` from the existing
   [data-dx-reveal] observer. Below 760px the three blocks stack and the
   wires are hidden.
   -------------------------------------------------------------------------- */
.dx-eco {
    --acc: #4d82ee;
    --txt: #0f172a; --faint: #8d97a8; --eyebrow: #2f6bd6;
    --chip: #ffffff; --chip-line: rgba(15, 23, 42, .12);
    --soft: rgba(15, 23, 42, .24); --soft-box: rgba(15, 23, 42, .06); --soft-ico: rgba(15, 23, 42, .45);
    --soon-tx: #6b7688;
    --core-tx: #0f172a; --core-sub: #4c5a70; --core-mark: #2f6bd6;
    --core-border: rgba(47, 107, 214, .35);
    --glow-a: rgba(58, 123, 255, .40); --glow-a2: rgba(54, 115, 238, .10);
    --glow-b: rgba(82, 139, 255, .30); --glow-b2: rgba(58, 123, 255, .08);
    /* 2026-09-23: the outer stop must be the SAME hue at zero alpha. The
       `transparent` keyword is rgba(0,0,0,0), so a gradient running into
       it interpolates through transparent black and leaves a visible
       grey rim — which is what made this glow's edge read as a hard
       boundary, unlike the hero and the overview bloom. */
    --glow-a3: rgba(54, 115, 238, .05);
    --glow-fade: rgba(54, 115, 238, 0);
    --wire: rgba(77, 130, 238, .5); --wire-soft: rgba(15, 23, 42, .22);
    font-family: 'Noto Sans JP', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic', sans-serif;
    color: var(--txt);
    position: relative;
    max-width: 1180px;
    margin: 0 auto;
    padding: 32px 0;
    display: grid;
    grid-template-columns: minmax(200px, 280px) minmax(0, 1fr) minmax(200px, 280px);
    column-gap: 40px;
    align-items: center;
}
html.dark .dx-eco {
    --txt: #e7e9ef; --faint: #5b6072; --eyebrow: #9dc0fb;
    --chip: #141824; --chip-line: rgba(255, 255, 255, .09);
    --soft: rgba(255, 255, 255, .22); --soft-box: rgba(255, 255, 255, .07); --soft-ico: rgba(255, 255, 255, .4);
    --soon-tx: #5b6072;
    --core-tx: #ffffff; --core-sub: #c3cddd; --core-mark: #ffffff;
    --core-border: rgba(157, 192, 251, .35);
    --glow-a: rgba(58, 123, 255, .42); --glow-a2: rgba(54, 115, 238, .12);
    --glow-b: rgba(82, 139, 255, .30); --glow-b2: rgba(58, 123, 255, .08);
    --glow-a3: rgba(58, 123, 255, .06);
    --glow-fade: rgba(58, 123, 255, 0);
    --wire: rgba(77, 130, 238, .42); --wire-soft: rgba(255, 255, 255, .22);
}

/* columns */
.dx-eco__col { position: relative; z-index: 1; display: grid; gap: 0; align-content: center; }
.dx-eco__eyebrow { margin: 0 0 8px; font-size: 13px; font-weight: 700; letter-spacing: .3em; color: var(--eyebrow); }
.dx-eco__soon { margin: 16px 0 6px; font-size: 11px; font-weight: 700; letter-spacing: .25em; color: var(--faint); }

/* chips: minimal rows (no boxes), hairline between rows */
.dx-eco__chip {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 40px;
    padding: 7px 0;
    font-size: 15px;
    font-weight: 400;
    white-space: nowrap;
    color: var(--txt);
}
.dx-eco__chip + .dx-eco__chip { border-top: 1px solid var(--chip-line); }
.dx-eco__soon + .dx-eco__chip { border-top: 0; }
.dx-eco__chip i {
    flex: none;
    width: 26px;
    height: 26px;
    border-radius: 7px;
    display: grid;
    place-items: center;
    background: var(--c, var(--acc));
}
.dx-eco__chip i svg { width: 15px; height: 15px; fill: none; stroke: #fff; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.dx-eco__chip--soon { color: var(--soon-tx); }
.dx-eco__chip--soon i { background: var(--soft-box); }
.dx-eco__chip--soon i svg { stroke: var(--soft-ico); }
.dx-eco__chip--plain { position: relative; min-height: 34px; font-size: 14px; padding-left: 38px; }
.dx-eco__chip--plain::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 50%;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--soft);
    transform: translateY(-50%);
}

/* core */
.dx-eco__core {
    position: relative;
    z-index: 1;
    justify-self: center;
    width: min(100%, 360px);
    aspect-ratio: 444 / 282;
    border-radius: 20px;
    background: transparent;
    display: grid;
    place-content: center;
    text-align: center;
    gap: 6px;
    border: 1.5px solid var(--core-border);
}
.dx-eco__core::before,
.dx-eco__core::after {
    content: "";
    position: absolute;
    z-index: -1;
    border-radius: 50%;
    pointer-events: none;
}
.dx-eco__core::before {
    inset: -70%;
    background: radial-gradient(circle, var(--glow-a) 0%, var(--glow-a2) 34%, var(--glow-a3) 56%, var(--glow-fade) 100%);
    /* 2026-09-23: a gradient alone still ends somewhere, and the eye finds
       that ring. Blur the layer as well so the falloff has no last stop to
       see — the same trick the hero's scrim relies on. */
    filter: blur(44px);
    animation: dx-eco-breathe 5s ease-in-out infinite alternate;
}
.dx-eco__core::after {
    inset: -52%;
    background: radial-gradient(circle, var(--glow-b) 0%, var(--glow-b2) 38%, var(--glow-a3) 60%, var(--glow-fade) 100%);
    filter: blur(34px);
    animation: dx-eco-breathe-b 7s ease-in-out infinite alternate;
}
@keyframes dx-eco-breathe {
    0%   { transform: translate(-10%, -8%) scale(.80); opacity: .55; }
    50%  { transform: translate(12%, 9%) scale(1.30);  opacity: 1; }
    100% { transform: translate(-8%, 12%) scale(1.06); opacity: .75; }
}
@keyframes dx-eco-breathe-b {
    0%   { transform: translate(10%, 7%) scale(1.18) rotate(0deg);    opacity: .45; }
    50%  { transform: translate(-13%, -10%) scale(.78) rotate(20deg); opacity: 1; }
    100% { transform: translate(8%, -12%) scale(1.34) rotate(-15deg); opacity: .6; }
}
.dx-eco__core svg { width: 44px; height: 44px; margin: 0 auto; fill: var(--core-mark); }
.dx-eco__core b { display: block; font: 500 34px/1.1 'Cormorant Garamond', Garamond, serif; letter-spacing: .2em; color: var(--core-tx); }
html.dark .dx-eco__core svg { filter: drop-shadow(0 0 10px rgba(160, 200, 255, .7)); }
html.dark .dx-eco__core b { text-shadow: 0 0 12px rgba(160, 200, 255, .6); }
.dx-eco__core p { margin: 6px 0 0; font-size: 14px; line-height: 1.5; font-weight: 500; color: var(--core-sub); }

/* connector overlay (drawn by script.js) */
.dx-eco__wires { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
.dx-eco__wires path { fill: none; stroke: var(--wire); stroke-width: 1.6; }
.dx-eco__wires path.soft { stroke: var(--wire-soft); stroke-dasharray: 6 5; stroke-width: 1.4; }
.dx-eco__wires circle { fill: var(--acc); }
.dx-eco__wires circle.soft { fill: var(--wire-soft); }
.dx-eco__wires circle.hub-ring { fill: none; stroke: var(--acc); stroke-opacity: .45; stroke-width: 2; }

/* Same as the overview section: the chips enter from ±40px, so clip the
   horizontal overflow they cause before they are revealed (2026-09-24). */
.dx-eco {
    overflow-x: clip;
}

/* entrance */
.dx-eco__chip {
    opacity: 0;
    transform: translateX(var(--from, -40px));
    transition: opacity .5s ease, transform .6s cubic-bezier(.2, .7, .2, 1);
    transition-delay: calc(var(--i, 0) * 90ms);
}
.dx-eco__col--r .dx-eco__chip { --from: 40px; }
.dx-eco.is-visible .dx-eco__chip { opacity: 1; transform: none; }
.dx-eco__eyebrow,
.dx-eco__soon { opacity: 0; transform: translateY(6px); transition: opacity .4s ease, transform .4s ease; }
.dx-eco.is-visible .dx-eco__eyebrow,
.dx-eco.is-visible .dx-eco__soon { opacity: 1; transform: none; }
.dx-eco__core { opacity: 0; transform: scale(.92); transition: opacity .6s ease .15s, transform .7s cubic-bezier(.2, .7, .2, 1) .15s; }
.dx-eco.is-visible .dx-eco__core { opacity: 1; transform: none; }
.dx-eco__wires { opacity: 0; transition: opacity .6s ease .9s; }
.dx-eco.is-visible .dx-eco__wires { opacity: 1; }

/* stacked layout (phone / narrow tablet) */
@media (max-width: 760px) {
    /* The core's breathing glow (inset -45%, scaled up to 1.34×) reaches
       well past a full-width core on phones. The diagram goes full-bleed
       (negative margin out to the viewport edges, equal padding back in,
       so the content keeps its column) and is clipped horizontally there:
       the glow runs to the screen edges without widening the page, and
       still spills above and below. `hidden` is the fallback for engines
       without `clip`. */
    .dx-eco {
        grid-template-columns: 1fr;
        row-gap: 26px;
        margin-inline: calc(50% - 50vw);
        padding-inline: calc(50vw - 50%);
        overflow-x: hidden;
        overflow-x: clip;
    }
    .dx-eco__core { order: 2; }
    .dx-eco__col--r { order: 3; }
    .dx-eco__wires { display: none; }
    .dx-eco__chip { white-space: normal; }
    /* Stacked: headings and rows centred on the page's centre line, like
       the overview core diagram; the coming-soon dot moves inline before
       the text. */
    .dx-eco__eyebrow,
    .dx-eco__soon { text-align: center; }
    .dx-eco__chip { justify-content: center; text-align: center; }
    .dx-eco__chip--plain { padding-left: 0; }
    .dx-eco__chip--plain::before {
        position: static;
        flex: none;
        transform: none;
    }
}
@media (prefers-reduced-motion: reduce) {
    .dx-eco__core::before,
    .dx-eco__core::after { animation: none; }
    .dx-eco__chip,
    .dx-eco__wires,
    .dx-eco__core,
    .dx-eco__eyebrow,
    .dx-eco__soon { opacity: 1; transform: none; transition: none; }
}

/* --------------------------------------------------------------------------
   フルブリード画像バンド
   セクション説明用の広い帯画像(Overview のコンセプト図、Plugins の管理画面など)。
   高さ固定 + object-fit: contain で、画像が幅より細ければ左右にレターボックスを残す。
   .dx-fullbleed と組み合わせるとビューポート端まで広がる(content コンテナを抜ける)。
   -------------------------------------------------------------------------- */

.dx-band {
    position: relative;
    width: 100%;
    height: clamp(260px, 32vw, 440px);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    text-align: center;
    color: var(--dx-accent);
    background:
        repeating-linear-gradient(45deg, color-mix(in srgb, var(--dx-accent) 8%, transparent) 0 14px, transparent 14px 28px),
        var(--dx-bg-soft);
    border: 1px solid var(--dx-accent-line);
    border-radius: 1rem;
    margin: 0 0 2.5rem;
}

.dx-band__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
    display: block;
    background: var(--dx-bg-soft);
}

.dark .dx-band__img {
    background: #0a0a12;
}

/* 端から端まで広げる: 親が中央寄せのコンテナのときに使用 */
.dx-fullbleed {
    width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    border-radius: 0;
    border-left: none;
    border-right: none;
}

/* ==========================================================================
   Evidence bar — hero 直下に置く受け皿ピル(script.js が JS 経由で inject)
   Added 2026-08-07 — brand-site delivery (C-2)
   Placement: hero title 側の .text-center 内、CTA の直下
   Rows: 2 行構造(3 + 4)
     - Row 1: v0.1 / AGPL-3.0 / 商用ライセンス(準備中)  — licensing / versioning
     - Row 2: PHP 8.2+ / Built on Laravel / Packagist / SECURITY.md — tech proof
   商用ピルは --soon 変種(dim + 非クリック)。初期リリースでは商用未提供のため。
   Theme を fork しない構成: theme はそのまま、HTML は script.js が組み立てる。
   ========================================================================== */
.dx-evidence-bar {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1.75rem;
}

.dx-evidence-bar__row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
}

.dx-evidence-bar__pill {
    display: inline-flex;
    align-items: center;
    padding: 0.32rem 0.75rem;
    font-family: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--dx-accent);
    background: transparent;
    border: 1px solid var(--dx-accent-line);
    border-radius: 999px;
    text-decoration: none;
    white-space: nowrap;
    line-height: 1;
    transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}

a.dx-evidence-bar__pill:hover {
    border-color: var(--dx-accent);
    background: var(--dx-accent-soft);
}

/* Non-clickable variant: for the commercial-license pill until the
   commercial license actually exists. Reads as "info, not link" —
   no hover reaction, muted color, softer border. */
.dx-evidence-bar__pill--soon {
    color: var(--dx-fg-muted);
    border-color: var(--dx-border);
    cursor: default;
}

/* ==========================================================================
   Hero / Overview / Ecosystem visuals — dark ⇄ light picture swap
   Added 2026-08-10 — brand-site delivery
   Each visual ships as 2 <picture> elements (light + dark). PC vs mobile
   is handled inside each <picture> via <source media="(min-width: 768px)">.
   Locale (JA / EN) is server-side (custom hero.blade.php picks the URL
   set by locale; content.html / content.en.html are separate pages).
   This class pair does the client-side dark/light toggle — sync with the
   theme's html.dark class so the visual follows the user's manual
   appearance toggle (not just prefers-color-scheme).
   Applied to:
     - .dx-hero-visual  (custom theme hero override)
     - .dx-overview-visual  (content.html #overview)
     - .dx-ecosystem-visual  (content.html new #ecosystem section)
   ========================================================================== */
.dx-hero-visual,
.dx-overview-visual,
.dx-ecosystem-visual {
    display: block;
    max-width: 100%;
    max-height: 100%;
}

/* Hero-only: give the <picture> a definite height so the inner <img>'s
   `max-h-full` (max-height: 100%) actually resolves. Percentage
   max-height on a child computes to `none` when the parent has only
   max-height (not `height`). Without this, the img renders at its
   natural 780px and the section's overflow-hidden clips the bottom
   of the picture. Overview / Ecosystem don't need this — they sit in
   auto-height sections, not vh-boxed containers, so their natural
   sizing is what we want there. */
.dx-hero-visual {
    height: 100%;
}

.dx-hero-visual--dark,
.dx-overview-visual--dark,
.dx-ecosystem-visual--dark {
    display: none;
}

html.dark .dx-hero-visual--light,
html.dark .dx-overview-visual--light,
html.dark .dx-ecosystem-visual--light {
    display: none;
}

html.dark .dx-hero-visual--dark,
html.dark .dx-overview-visual--dark,
html.dark .dx-ecosystem-visual--dark {
    display: block;
}

/* Sizing for the img inside each <picture>. Fill the picture box
   entirely with `width: 100%; height: 100%` so `object-fit:
   contain` has explicit dimensions to fit against — the image
   scales down inside those bounds preserving aspect, and (crucially)
   `object-position: 50% 50%` (default) VERTICALLY CENTRES the
   image within the box. Without this, the img has `w-auto h-auto`
   from Tailwind and sits at the picture element's top edge (block
   flow), which pushed the hero image to the top of its column in
   the 2-col layout and misaligned it from the text column that
   flex-centres its own content. Overrides Tailwind's `.w-auto` /
   `.h-auto` via higher-specificity selector (0,1,1 vs 0,1,0). */
.dx-hero-visual[style*="display: none"] img,
.dx-hero-visual--light img,
.dx-hero-visual--dark img,
.dx-overview-visual--light img,
.dx-overview-visual--dark img,
.dx-ecosystem-visual--light img,
.dx-ecosystem-visual--dark img {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* ==========================================================================
   Hero layout — vh-based vertical control (2026-08-11)
   Symptom before this block: on wide-but-short viewports (typical laptop
   ~700-900px tall), the text block (title + subtitle + buttons + evidence
   bar) consumed most of the available vertical space and left only a
   sliver for the hero foreground picture. On mobile, the portrait-format
   images fit poorly in what tiny space remained. Fixes:
     (1) reserve a vh range for the picture container (min 30vh, max 50vh)
         so the image is guaranteed a floor and capped from dominating
     (2) tighten the text-block gaps to vh so they scale with viewport
     (3) short-viewport variant (≤700px) shrinks the picture range so
         the hero still fits without clipping text
   The custom hero.blade.php also switches the mobile <img> src to the
   PC-format asset (landscape) instead of the portrait mobile asset,
   which packs the visual into narrow mobile viewports more efficiently.
   ========================================================================== */

/* Give .dx-hero-foreground a DEFINITE height (not just max-height)
   so the child <img>'s `max-h-full` (max-height: 100%) resolves to
   a real pixel value. Per CSS spec, a percentage max-height on a
   child computes to `none` when the parent has no explicit height —
   which was silently letting the hero picture render at its natural
   size and get clipped by the section's overflow-hidden. Definite
   height = the img is height-limited cleanly and stays inside. */
.dx-hero-foreground {
    height: 40vh;
    overflow: hidden;
    /* Space between the evidence bar and the picture. Wider on
       mobile/tablet so the picture reads as a distinct block below
       the CTAs/badges instead of butting right up against them.
       Overridden by min-width breakpoints below and reset to 0 in
       the 2-column layout at PC (the picture no longer sits below
       the text — it sits beside it — so a top gap would just push
       it off-centre). */
    margin-top: 4vh;
}

/* Tablet: still needs breathing room but a bit less than mobile
   since the text block occupies less of the viewport. */
@media (min-width: 640px) {
    .dx-hero-foreground {
        margin-top: 3vh;
    }
}

/* ==========================================================================
   Hero 2-column layout (PC ≥1025px) — 2026-08-11
   Title band stays full-width centred on all viewports so the JA
   couplet keeps its symmetric rhythm. Below the title, `.dx-hero-body`
   collapses (text → picture) on mobile and splits (picture-left |
   text-right) on PC via `flex-direction: row-reverse`. DOM order is
   (text → picture) so mobile reading order is natural and no `order:`
   overrides are needed.
   ========================================================================== */
.dx-hero-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* Fill the section's remaining vertical space (below the title
       band) so mobile/tablet content is centred against the section
       height rather than crammed at the top. Previously provided by
       Tailwind's `flex-1 min-h-0` classes on the element — those
       have been removed from the blade to keep Tailwind's flex-1
       from fighting the PC 2-col layout's explicit `flex: none`
       height override. Restated here in plain CSS so mobile
       behaviour is preserved without depending on utility classes
       whose cascade order we don't fully control. */
    flex: 1 1 0%;
    min-height: 0;
    justify-content: center;
    row-gap: 0;
    column-gap: 0;
}

/* PC 2-col: `identical explicit column heights` pattern.
   All previous attempts (flex + align-items:center, grid + auto rows,
   grid + 1fr rows, stretch + inner centre) produced inconsistent
   vertical alignment because they relied on the parent's centring
   machinery to keep the two columns' content on the same Y — which
   in practice failed on real browsers at various viewport
   sizes/heights (verified 2026-08-11 across multiple screenshots).

   Rebuilt on a simpler premise: if both columns have the SAME
   explicit height, and each column is itself a flex container
   centring its own content, then both content centres MUST land on
   the same Y — regardless of the body's own height, the parent's
   align-items behaviour, or any browser flex/grid quirk. The whole
   dependency on outer alignment is removed.

   Both columns get `height: 65vh` (same value as the picture
   container). Text column has ~280px of content centred inside 65vh.
   Picture column has `.dx-hero-foreground` at 65vh that fills the
   column exactly.

   Fixed widths (not flex-basis %) for deterministic layout across
   viewport widths. */
/* PC 2-col: `explicit body height + consistent flex-column columns`.
   Previous attempts (align-items:center, grid rows, align-content,
   stretch-and-centre, explicit column height with mixed flex
   directions) all failed at some viewport. The theory each time
   was correct, but real browsers rendered the two columns with
   different content centres by amounts that scaled with viewport
   height (~90px medium / ~230px narrow / ~290px tall).

   The one remaining variable to eliminate: the body's own height.
   Tailwind's `flex-1 min-h-0` on body makes it grow to fill the
   flex parent (container) — its height depends on the section /
   container / title chain, which the browser resolves in ways that
   apparently interact badly with a child grid/flex trying to align
   items by height.

   Fix: pin the body's height explicitly at PC via `flex: none` +
   `height: 65vh`, overriding the Tailwind flex-1 growth. Both
   columns then have the SAME simple flex-column layout with
   `justify-content: center` — identical config means identical
   vertical placement of their own content. No dependency on any
   outer align machinery. */
@media (min-width: 1025px) {
    /* At PC, centre the (title-band + body) group vertically inside
       the hero container instead of anchoring it to the top. Without
       this the container defaults to `justify-content: flex-start`,
       which pins the title band directly under the fixed nav padding
       and leaves all the leftover vertical space piled below the
       body — making the title read as "top-aligned" with a big
       visual void below the 2-col block. `justify-content: center`
       distributes that leftover space above the title and below the
       body equally, giving the whole hero a balanced vertical
       composition. */
    .dx-hero-container {
        justify-content: center;
    }

    .dx-hero-body {
        display: flex;
        flex-direction: row-reverse;
        justify-content: center;
        align-items: center;
        column-gap: 1.5rem;
        /* Override Tailwind `flex-1 min-h-0` (via `flex: none`) so
           body doesn't grow to fill container. Then set explicit
           height so both columns have a definite common reference.
           This is the crucial change vs the previous attempt — the
           columns' 55vh alone wasn't enough because they were
           still living inside a body of unpredictable height.
           `max-height` caps the vh growth on very tall viewports
           (e.g. rotated 27" monitor, ultrawide portrait) so the
           picture cell doesn't grow past the image's rendered
           height and leave large whitespace above/below the image. */
        flex: none;
        height: 65vh;
        max-height: 40rem;
    }

    .dx-hero-body__picture,
    .dx-hero-body__text {
        /* IDENTICAL flex-column config on both columns so vertical
           content centring is the exact same mechanism on each.
           Explicit `height: 65vh` (NOT `height: 100%`) — percentage
           heights on flex items are famously unreliable across
           browsers, especially with height:100% chains through
           multiple flex containers. Using the concrete vh value
           at every level of the chain (body / column / foreground)
           removes the resolution question entirely.
           `max-height` matches the body cap so both cells stay in
           sync with the body's own upper bound on tall viewports. */
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 65vh;
        max-height: 40rem;
        flex: 0 0 auto;
    }

    .dx-hero-body__picture {
        /* Picture takes remaining width after the fixed text column
           and the column gap. */
        width: calc(100% - 30rem - 1.5rem);
    }

    .dx-hero-body__text {
        /* Text column 30rem (480px) across all PC widths. The JA
           subtitle first sentence "IPA・NIST・OWASP が推奨する
           15項目を追加設定なしで。" needs ~470px at text-lg to fit
           on one line — 30rem gives ~10px margin. Second sentence
           "日本発のオープンソース CMS ／ Built on Laravel" (~400px)
           also fits comfortably, so the subtitle renders as the
           intended 2 lines instead of `balance`-wrapping the first
           sentence into an unbalanced 3-line block. Used to be
           26rem at ≥1280 + 30rem override at 1024-1279; consolidated
           to 30rem globally at PC since narrower text still failed
           at large viewports (the container caps at 2xl=1536 so the
           picture side has plenty of room even with a wider text). */
        width: 30rem;
        max-width: 30rem;
    }

    /* Picture container: explicit 65vh with matching max-height cap
       (mirrors body + columns so the whole chain shares the same
       upper bound on tall viewports).
       No top-margin — the 2-col layout centres the picture
       vertically, no gap to CTAs needed. */
    .dx-hero-foreground {
        margin-top: 0;
        height: 65vh;
        max-height: 40rem;
    }
}

/* Short-viewport (laptop, ~700-800px tall): shrink the picture
   area proportionally so text + picture + padding fit within
   100vh without clipping. */
@media (max-height: 800px) {
    .dx-hero-foreground {
        height: 35vh;
    }
}

@media (max-height: 700px) {
    .dx-hero-foreground {
        height: 30vh;
    }
}

/* PC 2-col overrides for short viewports — the mobile/tablet
   max-height rules above shrink the picture assuming a stacked
   text-then-picture layout that has to fit vertically. In 2-col
   at PC, picture and text SHARE vertical space, so the shrinkage
   isn't needed on the picture axis and just wastes the column
   width the image could use. Reinstate a larger budget at PC and
   step down more gently. Declared last so they win over the
   width-agnostic max-height rules above. */
@media (min-width: 1025px) and (max-height: 800px) {
    .dx-hero-foreground {
        height: 50vh;
    }
}

@media (min-width: 1025px) and (max-height: 700px) {
    .dx-hero-foreground {
        height: 45vh;
    }
}

/* Very short viewports (rotated tablets etc.) — accept a smaller
   picture rather than a clipped one. */
@media (max-height: 550px) {
    .dx-hero-foreground {
        height: 22vh;
        margin-top: 0.5vh;
    }
}

/* Hero text-block spacing / sizing tuned per viewport (2026-08-11):
     - Mobile: tighten title→subtitle to almost nothing (0.5vh), open
       subtitle→button to 3vh so the CTA doesn't sit on the description
     - PC (≥768px): reduce title font-size from the theme's text-6xl
       (3.75rem) to 3rem so the text block leaves more vertical room
       for the hero picture; title/subtitle margins moderate (1vh/2vh)
     - PC short (≥768px + ≤700px height): tightest set to fit both
       text and picture in a single viewport without clipping
   Selectors: title lives in `.dx-hero-title-band` (its own row for
   the 2-col layout), subtitle+CTAs live in `.dx-hero-text-block`
   (renamed conceptually to "text below title"). Both hold enough
   specificity to override Tailwind utilities without !important. */
.dx-hero-title-band h1.font-heading-hero {
    margin-bottom: 0.5vh;
}

/* Mobile / tablet: push the title band down from the top of the
   section so it doesn't hug the fixed nav padding. On PC we let the
   container's `justify-content: center` handle vertical distribution
   (the whole title + body group gets centred), but at mobile/tablet
   the body has `flex: 1 1 0%` growing to fill the container, so
   there is no leftover space for justify-content to redistribute
   around the title. Add explicit margin-top to the title band
   itself instead. Value chosen empirically: ~4vh gives enough
   breathing room above the couplet without shrinking the vertical
   room for the picture that follows below the text stack. */
@media (max-width: 1024px) {
    .dx-hero-title-band {
        margin-top: 4vh;
    }
}

.dx-hero-text-block > p {
    margin-bottom: 3vh;
}

/* Mobile subtitle: drop one step from the theme's `text-base` (1rem =
   16px) to 0.875rem (14px). At 14px the JA first sentence "IPA・NIST・
   OWASP が推奨する15項目を追加設定なしで。" fits on one line at ≥360px
   viewports, giving a 2-line subtitle total (sentence 1 + sentence 2)
   instead of the current 3-line wrap. EN also compresses proportionally
   — it may still wrap to 3 lines because its first sentence is longer,
   but the total block height still comes down. */
@media (max-width: 767px) {
    .dx-hero-text-block > p {
        font-size: 0.875rem;
    }
}

@media (min-width: 768px) {
    .dx-hero-title-band h1.font-heading-hero {
        /* Bumped 3rem → 3.5rem 2026-08-11: title reads a bit small
           relative to the picture below it at the 3rem size, and
           the 2-col layout freed up the vertical room to grow it. */
        font-size: 3.5rem;
        margin-bottom: 1vh;
    }
    .dx-hero-text-block > p {
        /* Widen subtitle→buttons gap on PC — 2-col layout has more
           room to breathe so the tighter mobile spacing reads cramped
           here. */
        margin-bottom: 3vh;
    }
}

@media (min-width: 768px) and (max-height: 700px) {
    .dx-hero-title-band h1.font-heading-hero {
        margin-bottom: 0.5vh;
    }
    .dx-hero-text-block > p {
        margin-bottom: 2vh;
    }
}

/* PC evidence bar: widen the gap above the bar so it reads as a
   distinct proof-strip below the CTAs instead of stacking tight
   against the button row. Only at ≥1025px (2-col layout) — on
   mobile/tablet the default 1.75rem is fine. */
@media (min-width: 1025px) {
    .dx-evidence-bar--hero {
        margin-top: 3vh;
    }
}

/* ==========================================================================
   Hero — single-column variant (2026-09-13)

   `.dx-hero-body--single` is set by hero.blade.php when the foreground
   <picture> is switched off ($heroShowPicture = false). With the
   picture gone the 2-column PC block above has nothing for its left
   column, so this modifier collapses the body back to one centred
   column at every viewport and lets the pentagon wormhole + Dixlase
   watermark (.dx-hero-bg-worm / -mark, centred at 50%) be the visual
   instead. Subtitle, CTAs and evidence bar all live inside
   `.dx-hero-body__text` (already text-center; the bar's own rules
   centre its rows), so centring the column centres them all.

   Vertical composition: the body stops stretching (flex: none) so the
   title band + body form one compact block, and the container centres
   that block over the wormhole — the scrim (.dx-hero-bg-scrim, also
   at 50%) mutes the pentagons and labels passing behind it, and the
   5% watermark reads through. Same stacking the prototype used.

   Overrides only what the ≥1025px block sets; the base (<1025px)
   layout is already a centred column. Kept as a modifier rather than
   editing the 2-col rules so flipping $heroShowPicture back restores
   the old layout with no CSS change.
   ========================================================================== */
.dx-hero-body--single {
    flex: 0 0 auto;
    padding-bottom: 2vh;
}
/* Split layout (2026-09-13): title band pinned to the top, body
   (subtitle / CTAs / evidence bar) pinned to the bottom, and the space
   between them left open for the wormhole and the watermark — both
   absolute layers centred on the section's 50% / 50%. With just those
   two children, space-between is the whole mechanism; an earlier
   revision had a flex spacer between them holding the mark, but the
   operator wants the mark exactly on the pentagons' spawn point, so
   it went back to an absolute layer and the spacer was removed. */
.dx-hero-container:has(.dx-hero-body--single) {
    justify-content: space-between;
}
@media (max-width: 1024px) {
    /* base rule gives the title band 4vh; 5vh here so the title is not
       glued to the header on phones (an earlier 2vh read as too high) */
    .dx-hero-container:has(.dx-hero-body--single) .dx-hero-title-band {
        margin-top: 5vh;
    }
}
/* Phones, single-column variant (2026-09-13): the copy block is the
   whole bottom of the hero now, so the two space-saving compromises
   made for the picture era are undone here —
   1. the CTAs sit side by side instead of stacked (Tailwind gives the
      row only from `sm:` / 640px); they shrink a little to fit two
      across a 360px viewport, wrapping if they still can't;
   2. the evidence bar shows all eight pills in its three rows again
      instead of the three-pill single row (`--mobile-hide` + rows as
      `display: contents` further down). Selectors carry
      `.dx-hero-body--single` so they outrank those rules on
      specificity regardless of order. */
@media (max-width: 639px) {
    .dx-hero .dx-hero-cta {
        flex-direction: row;
        flex-wrap: wrap;
        gap: .5rem;
    }
    .dx-hero .dx-hero-cta > a {
        height: 2.75rem;
        padding: 0 1rem;
        font-size: .9rem;
    }
}
@media (max-width: 767px) {
    /* Phones (2026-09-16): size the hero to the *small* viewport so the
       evidence bar is not pushed under the browser's bottom bar on iOS
       (100vh there is the large viewport), and tighten the stack from
       the sub-copy down so title, wormhole, copy, CTAs and all pills fit
       in one screen. 100vh stays as the fallback for engines without
       svh (Safari < 15.4, Chrome < 108). */
    .dx-hero {
        height: 100vh;
        height: 100svh;
    }
    .dx-hero-body--single {
        padding-bottom: 1vh;
    }
    .dx-hero-body--single .dx-hero-text-block > p {
        margin-bottom: 1vh;
        line-height: 1.55;
    }
    .dx-hero-body--single .dx-evidence-bar--hero {
        flex-direction: column;
        margin-top: .75rem;
        gap: .4rem;
    }
    .dx-hero-body--single .dx-evidence-bar--hero .dx-evidence-bar__row {
        display: flex;
    }
    .dx-hero-body--single .dx-evidence-bar__pill--mobile-hide {
        display: inline-flex;
    }
}
@media (min-width: 1025px) {
    /* a little air under the fixed header — flush against it read as
       "too high" on the live page */
    .dx-hero-container:has(.dx-hero-body--single) .dx-hero-title-band {
        margin-top: 5vh;
    }
    .dx-hero-body--single {
        flex-direction: column;
        column-gap: 0;
        /* the 2-col block gives .dx-hero-body a 65vh / 40rem box and
           centres its content inside; without the picture that box just
           floated the copy ~10rem above the bottom edge. Let the body
           wrap its content so the copy actually sits at the bottom. */
        height: auto;
        max-height: none;
    }
    .dx-hero-body--single .dx-hero-body__text {
        width: auto;
        max-width: 42rem;
        height: auto;
        max-height: none;
    }
}

/* ==========================================================================
   Hero decorative background pattern layers (2026-08-13)

   Adds 3 stacked pattern layers behind the hero content:
     0. `.dx-hero-bg-glow`     — two breathing blue ellipses on the shared
                                 centre — the hero's "circular gradient"
     1. `.dx-hero-bg-aurora`   — three drifting radial glows (mono blue by
                                 default; `.dx-hero--sunset` on the
                                 <section> restores amber / pink / emerald)
     2. `.dx-hero-bg-stars`    — twinkling starfield (dark mode only)
   (The five static concentric pentagons, `.dx-hero-bg-rings`, were
   removed on 2026-09-14 — the wormhole's own growing, rotating
   pentagons are the only ones now. Last version: commit f523d64.)
   plus the motion layers documented in their own block further down
   (mark / worm / scrim / freedom, 2026-09-10). The pentagon TILE
   layer that was #3 here from 2026-08-14 was removed on 2026-09-10
   when the wormhole landed.

   Each layer is absolutely positioned inside the hero <section> with
   z-index: 0 (above the theme's `.hero-bg-fill` gradient, below the
   content container which sits at z: 10). All layers use transparent
   / rgba fills so the theme's underlying gradient (primary-color
   radial → dark navy) shows through.

   Light / dark handling: since Dixlase uses `html.dark` for manual
   toggle, the BASE styles here are the LIGHT-mode values (visible
   without the .dark class) and `html.dark` overrides bring in the
   deep-space dark palette. This is inverted from the source CSS
   (which had dark as base + `.theme-light` overrides).

   Adapted from Claude Design's "Dixlase — Hero background" spec
   (2026-08-13); class names namespaced as `.dx-hero-bg-*` (single
   hyphen) to fit Dixlase naming instead of the source's `.dx-hero__*`
   (double underscore BEM). Content-layer classes (`.dx-hero-body`,
   `.dx-hero-title-band` etc.) already use single hyphen — consistent.
   ========================================================================== */
.dx-hero-bg-aurora,
.dx-hero-bg-stars {
    position: absolute;
    z-index: 0;
    pointer-events: none;
}

/* Palette + glow strengths — custom properties on the <section>, so the
   hue can be swapped without touching the size / position variants of
   the aurora below. Light values are the base; `html.dark` bumps them
   for the deep-space feel.

     --dx-glow-a / --dx-glow-b       the two breathing ellipses (.dx-hero-bg-glow)
     --dx-au1-core / --dx-au1-halo   aurora upper-left glow  (15% 18%)
     --dx-au2-core / --dx-au2-halo   aurora upper-right glow (88% 25%)
     --dx-au3-core / --dx-au3-halo   aurora bottom glow      (60% 100%)

   Default = MONO BLUE (operator request 2026-09-13, matching the
   hero.css prototype: one hue, deep-space field). The earlier sunset
   palette (amber / pink / emerald, 2026-08-13 v3) is preserved intact
   as the `.dx-hero--sunset` modifier further down — add that class to
   the <section> in hero.blade.php to bring it back. Each aurora glow
   keeps the v2 "tinted core + halo" two-stop shape so blends never go
   muddy grey. */
.dx-hero {
    /* 2026-09-24 (light): the centre glow carries the hero here too, so it
       is stronger than the corner pair; the bottom glow is off for the same
       reason as on the dark page — it lit the hero's lower edge and drew a
       seam against the first content section.
       2026-09-29 (2): the light page's hero was still far paler than the two
       sections below it — measured p90 chroma 23 against the shield's 34 and the
       ecosystem's 29. The hue was already the same; only the alphas were lower
       (glow .34/.22 against bloom-a .40 and eco glow-a .40), and the hero spreads
       its glow over 58rem ellipses, so the same alpha lands thinner. Raised the
       light hero's alphas by ~1.5x. Dark page untouched.
       2026-09-29 (3): still read as a pale wash. The lever was not the glow
       but the LIGHT SCRIM — a near-white veil (rgba(246,249,253,.74)) that
       sat over the centre and lifted everything back toward white. Took it
       to .30/.26/.16 and the glow to .74/.44. Measured on the rendered page:
       the lit area went rgb(216,227,249) L=91.1 chroma=34 -> rgb(191,210,247)
       L=86.0 chroma=56, which matches the density of the shield and the
       ecosystem bloom on the same page. The title stays black on L=86. */
    --dx-glow-a: rgba(54, 115, 238, .74);
    --dx-glow-b: rgba(58, 123, 255, .44);
    --dx-au1-core: rgba(95, 146, 251, .24);   /* blue-300 tint */
    --dx-au1-halo: rgba(54, 115, 238, .32);    /* Dixlase blue */
    --dx-au2-core: rgba(95, 146, 251, .20);
    --dx-au2-halo: rgba(54, 115, 238, .24);
    --dx-au3-core: rgba(95, 146, 251, 0);
    --dx-au3-halo: rgba(54, 115, 238, 0);
}
/* 2026-09-29: saturation matched to the OGP / SNS art as well. Measured
   the blue pixels of dixlase-ogp-1200x630 and the two teasers: hue 219,
   median (max-min)/(max+min) = .63. The page was at .33-.34, because the
   glow colours were pale tints (rgb(120,170,255) is .36, and the near-white
   cores .06-.23) and that ratio survives the alpha blend over near-black.
   Three blues now carry every glow, all at hue ~219.6: rgb(58,123,255)
   field/halo (.63), rgb(82,139,255) mid (.51), rgb(120,165,255) core (.36),
   with rgb(54,115,238) as the light-page field. Only glow declarations
   moved — wires, borders and accents keep #4d82ee.
   Previous: glow-a 112/168/255, glow-b 212/232/255, au core 224/238/255,
   au halo 104/158/255; shield bloom-a 99/160/255; eco glow-a 120/175/255. */
/* 2026-09-23: higher contrast on the dark page, matching the OGP / SNS
   cover art — the centre glow is the light source, the corners fall to
   near black. The glow pair roughly doubles; the three aurora corner
   glows come down so they no longer lift the whole field — but they
   stay visible as corner light, which is what gives the hero its depth
   (2026-09-23 follow-up: the first pass at a third was too dark).
   Previous values: glow .22/.14, au1 .22/.30, au2 .16/.22, au3 .12/.18. */
html.dark .dx-hero {
    --dx-glow-a: rgba(58, 123, 255, .92);
    --dx-glow-b: rgba(120, 165, 255, .62);
    --dx-au1-core: rgba(120, 165, 255, .22);   /* corners: keep the field dark */
    --dx-au1-halo: rgba(58, 123, 255, .16);
    --dx-au2-core: rgba(120, 165, 255, .17);
    --dx-au2-halo: rgba(58, 123, 255, .12);
    /* 2026-09-24: the bottom glow is off on the dark page. It lit the
       hero's lower edge, which then read as a seam against the first
       content section; leaving it black lets the two run together. */
    --dx-au3-core: rgba(120, 165, 255, 0);
    --dx-au3-halo: rgba(58, 123, 255, 0);
}
/* Mobile: the right and bottom aurora glows dominate a ~375px viewport
   at PC strength (see the size notes on the aurora below), so tone
   those two down here. */
@media (max-width: 767px) {
    .dx-hero {
        --dx-au2-core: rgba(95, 146, 251, .16);
        --dx-au2-halo: rgba(54, 115, 238, .17);
        --dx-au3-core: rgba(95, 146, 251, 0);
        --dx-au3-halo: rgba(54, 115, 238, 0);
    }
    html.dark .dx-hero {
        /* 2026-09-23: on a ~390px viewport the glow ellipses (58rem /
           46rem wide) cover the whole hero, so the section reads as one
           flat blue wash instead of a light source. Shrink both circles
           to roughly the viewport width and take the strength down a
           step; the corner aurora comes down with them, including the
           upper-left one that used the desktop value until now. */
        /* 2026-09-24: same brightness as the desktop centre glow (.92 / .62)
           — only the circles stay smaller, because the viewport is. The
           corner aurora follows the desktop values so the contrast reads
           the same on a phone. */
        --dx-glow-a: rgba(58, 123, 255, .92);
        --dx-glow-b: rgba(120, 165, 255, .62);
        --dx-au1-core: rgba(120, 165, 255, .18);
        --dx-au1-halo: rgba(58, 123, 255, .13);
        --dx-au2-core: rgba(120, 165, 255, .14);
        --dx-au2-halo: rgba(58, 123, 255, .10);
        --dx-au3-core: rgba(120, 165, 255, 0);
        --dx-au3-halo: rgba(58, 123, 255, 0);
    }
    /* The pair breathes up to ~1.4x, so the drawn size is well under the
       viewport at these values — that keeps the lit area a core rather than
       a wash, at the same strength as the desktop hero. */
    .dx-hero-bg-glow::before { width: 7.5rem; height: 6rem; }
    .dx-hero-bg-glow::after  { width: 5.5rem; height: 4.5rem; }
    html.dark .dx-hero-bg-scrim {
        background: radial-gradient(closest-side, rgba(3, 7, 18, .30) 0%, rgba(3, 7, 18, .26) 34%, rgba(3, 7, 18, .16) 62%, transparent 86%);
    }
}

/* Sunset palette (2026-08-13 v3) — opt-in. Amber upper-left as the warm
   anchor, pink upper-right with a bright hot core, emerald at the
   bottom as the cool counterpoint; a warm-warm-cool trio that read as
   sunset / aurora borealis over the blue underlay. Values are exactly
   what shipped before the mono-blue default landed, including the
   mobile tone-down (pink compact, emerald whispering). */
.dx-hero--sunset {
    --dx-au1-core: rgba(254, 243, 199, .10);   /* amber-100 (near-white amber) */
    --dx-au1-halo: rgba(251, 191, 36,  .14);   /* amber-400 */
    --dx-au2-core: rgba(249, 168, 212, .25);   /* pink-300 (medium-pink centre) */
    --dx-au2-halo: rgba(244, 114, 182, .15);   /* pink-400 halo */
    --dx-au3-core: rgba(167, 243, 208, .10);   /* emerald-200 */
    --dx-au3-halo: rgba(52,  211, 153, .16);   /* emerald-400 */
}
html.dark .dx-hero--sunset {
    --dx-au1-core: rgba(254, 243, 199, .22);
    --dx-au1-halo: rgba(251, 191, 36,  .26);
    --dx-au2-core: rgba(249, 168, 212, .42);   /* pink-300 (bright pink centre) */
    --dx-au2-halo: rgba(244, 114, 182, .26);
    --dx-au3-core: rgba(167, 243, 208, .22);
    --dx-au3-halo: rgba(52,  211, 153, .26);
}
@media (max-width: 767px) {
    .dx-hero--sunset {
        --dx-au2-core: rgba(249, 168, 212, .20);
        --dx-au2-halo: rgba(244, 114, 182, .11);
        --dx-au3-core: rgba(167, 243, 208, .05);
        --dx-au3-halo: rgba(52,  211, 153, .08);
    }
    html.dark .dx-hero--sunset {
        --dx-au2-core: rgba(249, 168, 212, .38);
        --dx-au2-halo: rgba(244, 114, 182, .20);
        --dx-au3-core: rgba(167, 243, 208, .12);
        --dx-au3-halo: rgba(52,  211, 153, .14);
    }
}

/* Glow — the hero's "circular gradient" (2026-09-13, ported from the
   prototype's .dx-hero__glow): two soft blue ellipses on the shared
   centre (--dx-hero-centre, so they sit on the wormhole's spawn point
   and follow the phone offset), each breathing on its own cycle — 8s
   and 11s, ease-in-out alternate — through translate / scale (and a
   little rotate on the second), so the two never line up and the
   centre never sits still. This replaces the static radial disc the
   theme's hero-bg-fill used to paint at the centre; hero.blade.php now
   paints a flat vertical navy ramp underneath ($heroFlatBase). */
.dx-hero-bg-glow {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
}
.dx-hero-bg-glow::before,
.dx-hero-bg-glow::after {
    content: "";
    position: absolute;
    left: 50%;
    top: var(--dx-hero-centre);
    width: 58rem;
    height: 42rem;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    will-change: transform, opacity;
}
.dx-hero-bg-glow::before {
    background: radial-gradient(closest-side, var(--dx-glow-a), transparent 100%);
    animation: dx-hero-bg-glow-breathe-a 8s ease-in-out infinite alternate;
}
.dx-hero-bg-glow::after {
    width: 46rem;
    height: 34rem;
    background: radial-gradient(closest-side, var(--dx-glow-b), transparent 100%);
    animation: dx-hero-bg-glow-breathe-b 11s ease-in-out infinite alternate;
}
@keyframes dx-hero-bg-glow-breathe-a {
    0%   { transform: translate(-50%, -50%) scale(0.80); opacity: .55; }
    50%  { transform: translate(-36%, -64%) scale(1.42); opacity: 1; }
    100% { transform: translate(-64%, -40%) scale(1.10); opacity: .75; }
}
@keyframes dx-hero-bg-glow-breathe-b {
    0%   { transform: translate(-50%, -50%) scale(1.24) rotate(0deg);   opacity: .4; }
    50%  { transform: translate(-68%, -32%) scale(0.72) rotate(22deg);  opacity: 1; }
    100% { transform: translate(-32%, -68%) scale(1.48) rotate(-16deg); opacity: .6; }
}

/* Aurora — three soft radial glows, drifting slowly. Colours come from
   the --dx-au* properties above; only the sizes / positions live here.

   Positions (container-relative → section-relative):
     15% 18%  → section (~8% X,  ~-6.6% Y) - deep in top-left corner
     88% 25%  → section (~96% X, ~2.5% Y)  - centre near top-right corner
     60% 100% → section (~62% X, 100% Y)   - centre at bottom edge, right of centre

   Container extends -10% left/right and -30% top of section, height
   130% — so container Y=0 sits above section top, and gradient
   centres near container edges push glows visibly past the section
   boundary (clipped by the section's overflow-hidden). This gives the
   "coming from off-screen edge" corner glow feel. */
.dx-hero-bg-aurora {
    inset: -30% -10% auto -10%;
    height: 130%;
    background:
        radial-gradient(42rem 28rem at 15% 18%,  var(--dx-au1-core) 0%, var(--dx-au1-halo) 22%, transparent 60%),
        radial-gradient(40rem 26rem at 88% 25%,  var(--dx-au2-core) 0%, var(--dx-au2-halo) 22%, transparent 62%),
        radial-gradient(48rem 32rem at 60% 100%, var(--dx-au3-core) 0%, var(--dx-au3-halo) 22%, transparent 62%);
    filter: blur(8px);
    animation: dx-hero-bg-aurora-drift 18s ease-in-out infinite alternate;
}

/* PC (≥768px): enlarge the upper-left and upper-right glows so they
   read as dramatic corner sweeps on wide viewports. Base sizes were
   tuned for mobile width; a wide PC viewport has room for them to
   spread. The bottom glow stays at 48×32rem — already generous. */
@media (min-width: 768px) {
    .dx-hero-bg-aurora {
        background:
            radial-gradient(52rem 36rem at 15% 18%,  var(--dx-au1-core) 0%, var(--dx-au1-halo) 22%, transparent 60%),
            radial-gradient(50rem 34rem at 88% 25%,  var(--dx-au2-core) 0%, var(--dx-au2-halo) 22%, transparent 62%),
            radial-gradient(48rem 32rem at 60% 100%, var(--dx-au3-core) 0%, var(--dx-au3-halo) 22%, transparent 62%);
    }
}

/* Mobile (<768px): the upper-right glow's 40×26rem span is huge
   relative to a ~375px width, so shrink it to 26×18rem (its opacity
   and the bottom glow's are also toned down via the palette
   variables above). The upper-left glow is left alone — 15% 18% puts
   most of it above the section edge on phones anyway. */
@media (max-width: 767px) {
    .dx-hero-bg-aurora {
        background:
            radial-gradient(42rem 28rem at 15% 18%,  var(--dx-au1-core) 0%, var(--dx-au1-halo) 22%, transparent 60%),
            radial-gradient(26rem 18rem at 88% 25%,  var(--dx-au2-core) 0%, var(--dx-au2-halo) 22%, transparent 62%),
            radial-gradient(48rem 32rem at 60% 100%, var(--dx-au3-core) 0%, var(--dx-au3-halo) 22%, transparent 62%);
    }
}

/* 2026-08-13: Y range shrunk from (-2%, +2%, +3%) to (0%, +1%, +0.5%).
   The original range had the 0% keyframe (which is the state on page
   load) translate the aurora 2% UPWARD. That shifted the bottom
   emerald glow's centre from section y=100% (base) up to section
   y=98% — putting the glow's brightest point 2% above the section
   bottom and leaving the bottom edge showing the gradient's
   transparent outer ring instead of the hot centre. As the
   animation progressed the aurora drifted DOWNWARD (Y going from
   -2% → +3% over 9 seconds), which read as "the black-cut bottom
   gradually fills in with green" — a distracting reveal that
   looked like a rendering bug.
   New range keeps X drift for the "alive" side-to-side motion but
   caps Y at 0% (base position, never up) → +1%. The emerald hot
   spot stays at or below section bottom throughout the cycle, so
   the visible upper half stays consistently bright and doesn't
   show the reveal artefact. */
@keyframes dx-hero-bg-aurora-drift {
    0%   { transform: translate3d(-3%, 0%,   0) scale(1); }
    50%  { transform: translate3d(3%,  1%,   0) scale(1.06); }
    100% { transform: translate3d(-2%, 0.5%, 0) scale(1.03); }
}

/* Starfield — 16 tiny point lights of two sizes/colors, masked away
   from the centre so they don't compete with the main content. Only
   rendered in dark mode (invisible on light backgrounds). */
.dx-hero-bg-stars {
    inset: 0;
    display: none;
}

html.dark .dx-hero-bg-stars {
    display: block;
    opacity: .9;
    background-image:
        radial-gradient(1.6px 1.6px at 12% 14%, rgba(255, 255, 255, .55), transparent 60%),
        radial-gradient(1.2px 1.2px at 27% 8%,  rgba(199, 222, 255, .45), transparent 60%),
        radial-gradient(1.8px 1.8px at 41% 22%, rgba(255, 255, 255, .40), transparent 60%),
        radial-gradient(1.2px 1.2px at 63% 11%, rgba(199, 222, 255, .50), transparent 60%),
        radial-gradient(1.5px 1.5px at 79% 19%, rgba(255, 255, 255, .42), transparent 60%),
        radial-gradient(1.1px 1.1px at 91% 9%,  rgba(199, 222, 255, .38), transparent 60%),
        radial-gradient(1.7px 1.7px at 7% 38%,  rgba(255, 255, 255, .38), transparent 60%),
        radial-gradient(1.2px 1.2px at 18% 55%, rgba(199, 222, 255, .34), transparent 60%),
        radial-gradient(1.4px 1.4px at 33% 71%, rgba(255, 255, 255, .30), transparent 60%),
        radial-gradient(1.1px 1.1px at 8% 82%,  rgba(199, 222, 255, .36), transparent 60%),
        radial-gradient(1.6px 1.6px at 24% 93%, rgba(255, 255, 255, .32), transparent 60%),
        radial-gradient(1.3px 1.3px at 88% 44%, rgba(199, 222, 255, .40), transparent 60%),
        radial-gradient(1.7px 1.7px at 95% 63%, rgba(255, 255, 255, .34), transparent 60%),
        radial-gradient(1.2px 1.2px at 73% 84%, rgba(199, 222, 255, .30), transparent 60%),
        radial-gradient(1.5px 1.5px at 58% 95%, rgba(255, 255, 255, .28), transparent 60%),
        radial-gradient(1.1px 1.1px at 46% 47%, rgba(199, 222, 255, .22), transparent 60%);
    -webkit-mask-image: radial-gradient(46% 42% at 50% 62%, transparent 0%, #000 78%);
            mask-image: radial-gradient(46% 42% at 50% 62%, transparent 0%, #000 78%);
    animation: dx-hero-bg-stars-twinkle 7s ease-in-out infinite alternate;
}

@keyframes dx-hero-bg-stars-twinkle {
    0%   { opacity: .62; }
    100% { opacity: 1; }
}

/* ==========================================================================
   Hero motion layers (2026-09-10) — ported from the operator's
   hero.css / hero.html / hero.js prototype ("pentagon wormhole").

   Four decorative layers on top of the aurora / rings / stars trio.
   The pentagon TILE layer that used to sit here was dropped per the
   operator — the wormhole's own pentagons carry that motif now.
     4. `.dx-hero-bg-mark`     — Dixlase D-mark + wordmark watermark,
                                 pinned to the section centre = the
                                 wormhole's spawn point
     5. `.dx-hero-bg-worm`     — 5 pentagons cycling through a 24s
                                 "wormhole": each grows + rotates from
                                 the vanishing point outward, carrying
                                 an LED dot pinned to one vertex and a
                                 value-pillar label (SECURITY /
                                 FAIRNESS / TRANSPARENCY /
                                 EXTENSIBILITY / SUSTAINABILITY)
     6. `.dx-hero-bg-scrim`    — radial scrim that mutes the rings and
                                 labels passing behind the copy
     7. `.dx-hero-bg-freedom`  — "FREEDOM" serif, blooming from the
                                 centre past the viewer once per cycle

   Two behaviours are driven from core/front/script.js via custom
   properties on the `.dx-hero` <section>:
     --dx-mx / --dx-my  (-.5 … .5)  pointer parallax
     --dx-depth         ( 0  … 1 )  how far the hero has scrolled out
                                    of view (fades + zooms the worm)

   Naming: the source used BEM `.dx-worm__*` / `.dx-hero__*`; renamed
   to the `.dx-hero-bg-*` family of the sibling layers, keeping `__`
   only for internal parts (same as `.dx-hero-body__text`). Light /
   dark inverted from the source (source: dark base +
   `.dx-hero--light`; here: light base + `html.dark`) to match the
   rest of this file.
   ========================================================================== */

/* The wormhole used to register --dx-s / --dx-r with @property and
   animate them from one keyframe track. Dropped 2026-09-16: iOS WebKit
   froze the pillar transform whenever the same keyframes also carried
   a registered custom property. Each element now runs its own
   keyframes made of standard properties only, phase-aligned through
   --dx-delay (set inline per pillar in the markup). */

/* Parallax / depth inputs — defaults so every layer renders sanely
   before script.js runs, with it disabled, or under reduced-motion.
   --dx-hero-centre is the one vertical anchor shared by the wormhole
   tube (= the pentagons' spawn point), the D-mark and FREEDOM, so they
   always move together.

   The values here are static fallbacks only: when the single-column
   split layout is active, script.js measures the real midpoint
   between the title band's bottom edge and the copy block's top edge
   and writes it to --dx-hero-centre inline (re-measured on resize and
   once web fonts settle), so the cluster sits centred in the open
   space on every viewport. The geometric 50% always lands low — the
   bottom copy block is taller than the title, and on phones the top
   ~110px are under the fixed header — hence the lifted fallbacks. */
.dx-hero { --dx-mx: 0; --dx-my: 0; --dx-depth: 0; --dx-hero-centre: 47%; }
@media (max-width: 767px) {
    .dx-hero { --dx-hero-centre: 41%; }
}

/* -- Watermark --------------------------------------------------------- */
/* The D-mark's own centre sits on the section's 50% / 50% — the point
   the wormhole tube spawns its pentagons from — and the wordmark hangs
   below it. The lockup is a column (D, gap, word), so centring the
   whole column would lift the D by (gap + word) / 2 above the spawn
   point; `top` adds exactly that back. The three sizes are custom
   properties so `top`, `gap` and the two children stay in lockstep.
   left has a rightward nudge of 9% of the D's width: the D's mass is
   in its left stem, so a geometrically centred mark reads left-heavy
   (the prototype's +18px was the same correction). Proportional so
   it is ~18px on a desktop D and ~7px on a phone one — a fixed
   1.25rem pushed the small phone mark visibly off-centre. z:1 —
   above the z:0 bg layers, below the z:10 content container. 5%
   opacity — a watermark, not a lockup.

   Sizes carry vh terms so the lockup shrinks on short viewports and
   the wordmark, which hangs below the pinned D, stays clear of the
   subtitle: the D also caps at (100vh - 52rem) — roughly the half
   height left once the bottom copy block (~22rem) and the wordmark
   are subtracted — so on a ~900px laptop it gives up size instead of
   running into the copy. The wordmark is deliberately the smaller
   partner (5vh, 4.5rem cap): its bottom edge is what meets the
   subtitle first. */
.dx-hero-bg-mark {
    --dx-mark-d:    clamp(5rem, min(16vw, 20vh, calc(100vh - 52rem)), 14rem);
    --dx-mark-gap:  clamp(.4rem, 1vw, .8rem);
    --dx-mark-word: clamp(1.5rem, min(6vw, 5vh), 4.5rem);
    position: absolute;
    left: calc(50% + var(--dx-mark-d) * 0.09);
    top: calc(var(--dx-hero-centre) + (var(--dx-mark-gap) + var(--dx-mark-word)) / 2);
    z-index: 1;
    pointer-events: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--dx-mark-gap);
    transform: translate(-50%, -50%) translate3d(calc(var(--dx-mx) * -6px), calc(var(--dx-my) * -6px), 0);
    opacity: .05;
}
/* Phones: the vw/vh terms already bottom out at the clamp minimums
   there, so set the sizes directly. On a 390×844 phone the pinned D
   (5rem) ends ~25px above the subtitle, so gap + wordmark must fit in
   that: .35rem + 1.25rem. */
@media (max-width: 767px) {
    .dx-hero-bg-mark {
        --dx-mark-d:    5rem;
        --dx-mark-gap:  .35rem;
        --dx-mark-word: 1.25rem;
    }
}
.dx-hero-bg-mark svg {
    display: block;
    width: var(--dx-mark-d);
    height: auto;
    fill: #0f172a;
}
.dx-hero-bg-mark span {
    font-family: "Cormorant Garamond", Garamond, Georgia, serif;
    font-size: var(--dx-mark-word);
    font-weight: 500;
    letter-spacing: .18em;
    text-indent: .18em;
    line-height: 1;
    color: #0f172a;
}
html.dark .dx-hero-bg-mark svg  { fill: #ffffff; }
html.dark .dx-hero-bg-mark span { color: #ffffff; }

/* -- Pentagon wormhole ------------------------------------------------- */
.dx-hero-bg-worm {
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    overflow: hidden;
    opacity: calc(1 - var(--dx-depth) * .85);
    transform: translate3d(calc(var(--dx-mx) * -16px), calc(var(--dx-my) * -16px), 0) scale(calc(1 + var(--dx-depth) * .12));
}
.dx-hero-bg-worm__tube {
    position: absolute;
    left: 50%;
    top: var(--dx-hero-centre);
    width: min(66vw, 720px);
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
}
/* Each pillar carries its OWN pentagon, sized by the same --dx-s that
   places its dot — one animation instance drives both, so the dot is
   always exactly on a corner. Nothing here is scaled by transform,
   so dot and word keep a constant size.
   Three keyframe tracks, one per element, all standard properties
   (iOS WebKit froze the pillar transform whenever the same keyframes
   also carried a registered custom property):
     pillar  drift + rotation (transform) and fade (opacity)
     poly    size (width / height) — the growing pentagon
     spot    vertex position (left / top) + counter-rotation
   All three share the 24s cycle and read the phase from --dx-delay,
   set inline per pillar next to animation-delay in the markup. */
.dx-hero-bg-worm__pillar {
    position: absolute;
    inset: 0;
    opacity: 0;
    animation: dx-hero-bg-worm-label 24s linear infinite;
    animation-delay: var(--dx-delay, 0s);
}
.dx-hero-bg-worm__poly {
    position: absolute;
    left: 50%;
    top: 50%;
    width: 100%;
    height: 100%;
    translate: -50% -50%;
    overflow: visible;
    animation: dx-hero-bg-worm-poly 24s linear infinite;
    animation-delay: var(--dx-delay, 0s);
}
@keyframes dx-hero-bg-worm-poly {
    0%   { width: 4%;   height: 4%; }
    30%  { width: 30%;  height: 30%; }
    55%  { width: 72%;  height: 72%; }
    80%  { width: 130%; height: 130%; }
    100% { width: 200%; height: 200%; }
}
/* The polygon's stroke attribute in the markup is the dark value;
   CSS wins over presentation attributes, so override per palette. */
.dx-hero-bg-worm__poly polygon           { stroke: #2f6bd6; }
html.dark .dx-hero-bg-worm__poly polygon { stroke: #9dc0fb; }
@keyframes dx-hero-bg-worm-label {
    0%   { transform: translate(0, 0)        rotate(0deg);  opacity: 0; }
    16%  { opacity: 1; }
    30%  { transform: translate(2.5%, -1.5%) rotate(7deg); }
    55%  { transform: translate(-2%, 1.8%)   rotate(14deg); opacity: 1; }
    80%  { transform: translate(2.2%, 2.4%)  rotate(21deg); opacity: .92; }
    100% { transform: translate(-0.8%, -2%)  rotate(26deg); opacity: 0; }
}
/* Zero-size marker pinned to the pentagon vertex (polygon r = 46% of
   the tube); it cancels the parent rotation so dot and word stay
   upright. --dx-dx / --dx-dy are that vertex's unit vector, set
   inline per pillar in the markup.
   Its keyframes move it along the vertex (left / top follow the same
   size stops as the pentagon) and counter-rotate it against the pillar
   (transform) — standard properties only; --dx-dx / --dx-dy are static
   per-pillar values, which var() resolves fine inside keyframes. */
.dx-hero-bg-worm__spot {
    position: absolute;
    width: 0;
    height: 0;
    z-index: 1;
    left: calc(50% + var(--dx-dx) * 46%);
    top:  calc(50% + var(--dx-dy) * 46%);
    animation: dx-hero-bg-worm-spot 24s linear infinite;
    animation-delay: var(--dx-delay, 0s);
}
@keyframes dx-hero-bg-worm-spot {
    0%   { left: calc(50% + var(--dx-dx) * 46% * .04); top: calc(50% + var(--dx-dy) * 46% * .04); transform: rotate(0deg); }
    30%  { left: calc(50% + var(--dx-dx) * 46% * .3);  top: calc(50% + var(--dx-dy) * 46% * .3);  transform: rotate(-7deg); }
    55%  { left: calc(50% + var(--dx-dx) * 46% * .72); top: calc(50% + var(--dx-dy) * 46% * .72); transform: rotate(-14deg); }
    80%  { left: calc(50% + var(--dx-dx) * 46% * 1.3); top: calc(50% + var(--dx-dy) * 46% * 1.3); transform: rotate(-21deg); }
    100% { left: calc(50% + var(--dx-dx) * 46% * 2);   top: calc(50% + var(--dx-dy) * 46% * 2);   transform: rotate(-26deg); }
}
/* LED: hard white core, tight hot halo, then a wide blue bloom */
.dx-hero-bg-worm__dot {
    position: absolute;
    left: -6px;
    top: -6px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #ffffff;
    box-shadow:
        0 0 0 3px #ffffff,
        0 0 8px 4px #ffffff,
        0 0 18px 6px rgba(234, 242, 255, 1),
        0 0 36px 10px rgba(157, 192, 251, .95),
        0 0 70px 18px rgba(77, 130, 238, .7),
        0 0 120px 30px rgba(77, 130, 238, .35);
}
.dx-hero-bg-worm__word {
    position: absolute;
    left: 0;
    top: 0;
    white-space: nowrap;
    font-style: normal;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .22em;
    color: #2f6bd6;
}
html.dark .dx-hero-bg-worm__word { color: #eef4ff; }
.dx-hero-bg-worm__pillar[data-side="r"] .dx-hero-bg-worm__word { translate: 16px -50%; }
.dx-hero-bg-worm__pillar[data-side="l"] .dx-hero-bg-worm__word { translate: calc(-100% - 16px) -50%; }
.dx-hero-bg-worm__pillar[data-side="t"] .dx-hero-bg-worm__word { translate: -50% calc(-100% - 14px); }

/* -- Scrim ------------------------------------------------------------- */
/* Mutes rings and labels passing behind the copy. Painted after the
   tube in DOM order; the watermark is z:1 so it still reads through. */
.dx-hero-bg-scrim {
    position: absolute;
    z-index: 0;
    pointer-events: none;
    left: 50%;
    top: 50%;
    width: min(124%, 1180px);
    height: 60%;
    transform: translate(-50%, -50%);
    background: radial-gradient(closest-side, rgba(246, 249, 253, .30) 0%, rgba(246, 249, 253, .26) 34%, rgba(246, 249, 253, .16) 62%, transparent 86%);
}
/* 2026-09-23: the scrim used to veil the centre almost opaquely, which
   swallowed the glow. Keep enough of it for the title to stay readable
   over the wormhole, but let the light through (was .9 / .82 / .55). */
html.dark .dx-hero-bg-scrim {
    background: radial-gradient(closest-side, rgba(3, 7, 18, .26) 0%, rgba(3, 7, 18, .22) 34%, rgba(3, 7, 18, .13) 62%, transparent 86%);
}

/* -- FREEDOM ----------------------------------------------------------- */
/* The arrival point: dead centre, large serif, blooming past the
   viewer. z:11 — deliberately ABOVE the z:10 content container so it
   sweeps over the copy for the ~5s it is visible each cycle (the
   source stacked it above `.dx-hero__inner` the same way);
   pointer-events none so it never intercepts the CTAs beneath. */
.dx-hero-bg-freedom {
    position: absolute;
    left: 50%;
    top: var(--dx-hero-centre);
    z-index: 11;
    pointer-events: none;
    white-space: nowrap;
    opacity: 0;
    font-family: "Cormorant Garamond", Garamond, Georgia, serif;
    font-size: clamp(2.1rem, 5.6vw, 4.2rem);
    font-weight: 500;
    letter-spacing: .3em;
    text-indent: .3em;
    color: #12336f;
    text-shadow: 0 0 26px rgba(255, 255, 255, .9), 0 0 60px rgba(77, 130, 238, .35);
    animation: dx-hero-bg-freedom-zoom 24s linear infinite;
    /* Intro sequencing (2026-09-14): hold the first bloom back so the
       five pillar words are on screen first. The prototype's inline
       -3.5s delay started FREEDOM 14% into its cycle on page load —
       mid-bloom, the first thing the viewer saw. 6s puts the first
       bloom (opacity 1 at 4% of the cycle ≈ +1s) at ~7s, after
       SECURITY has finished its ~3.8s fade-in and the other four
       pillars (which start mid-cycle) are established; the 24s loop
       repeats from there. Was an inline style on the <span>. */
    animation-delay: 6s;
}
html.dark .dx-hero-bg-freedom {
    color: #ffffff;
    text-shadow: 0 0 12px rgba(255, 255, 255, .9), 0 0 34px rgba(157, 192, 251, .95), 0 0 90px rgba(77, 130, 238, .65);
}
/* One continuous zoom: the scale keyframes lie on a single smooth
   curve, only opacity / blur envelope it. Visible ~4-27% of the cycle. */
@keyframes dx-hero-bg-freedom-zoom {
    0%   { transform: translate(-50%, -50%) scale(.16);  opacity: 0;  filter: blur(7px); }
    4%   { transform: translate(-50%, -50%) scale(.40);  opacity: 1;  filter: blur(0); }
    11%  { transform: translate(-50%, -50%) scale(.86);  opacity: 1;  filter: blur(0); }
    19%  { transform: translate(-50%, -50%) scale(1.50); opacity: .6; filter: blur(1.2px); }
    27%  { transform: translate(-50%, -50%) scale(2.30); opacity: 0;  filter: blur(6px); }
    100% { transform: translate(-50%, -50%) scale(2.30); opacity: 0;  filter: blur(6px); }
}

/* (The periodic light sweep over the primary (install) button — the
   `dx-hero-cta__primary` shimmer — was removed 2026-09-17 at the
   operator's request; the class hook stays in hero.blade.php.) */

/* -- Reduced motion ---------------------------------------------------- */
/* Park the cycles at a readable state: pillars semi-visible at full
   size, FREEDOM shown at rest. (Aurora / stars have
   their own reduced-motion rule in this file; script.js also skips
   the pointer / scroll bindings under this query.) */
@media (prefers-reduced-motion: reduce) {
    .dx-hero-bg-worm__pillar,
    .dx-hero-bg-worm__poly,
    .dx-hero-bg-worm__spot,
    .dx-hero-bg-freedom { animation: none; }
    .dx-hero-bg-worm__pillar { opacity: .55; }
    .dx-hero-bg-freedom { opacity: 1; transform: translate(-50%, -50%); filter: none; }
}

/* Respect reduced-motion preference — disable the drift/twinkle
   animations, keeping the static pattern visible. */
@media (prefers-reduced-motion: reduce) {
    .dx-hero-bg-aurora,
    .dx-hero-bg-stars,
    .dx-hero-bg-glow::before,
    .dx-hero-bg-glow::after { animation: none; }
}

/* ==========================================================================
   Evidence bar — mobile chip reduction (2026-08-11)
   The hero-injected evidence bar carries 7 pills spanning 2 rows. On
   mobile that eats vertical space that would otherwise show more of the
   hero picture. Reduce the mobile set to the 3 core pills (version /
   license / framework) and hide the rest via the mobile-hide modifier.
   Layout on mobile collapses the 2-row structure into a single wrap-row
   so the 3 remaining pills sit as one line. Hero-scoped (.dx-evidence-
   bar--hero) so the philosophy Manifest bar stays untouched (it shows
   all 7 pills on all viewports since space isn't at a premium there).
   ========================================================================== */
@media (max-width: 767px) {
    .dx-evidence-bar__pill--mobile-hide {
        display: none;
    }
    .dx-evidence-bar--hero {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
    }
    /* Rows become transparent to layout on mobile so the surviving pills
       flow as siblings under the parent flex container. */
    .dx-evidence-bar--hero .dx-evidence-bar__row {
        display: contents;
    }
}

