/* SPDX-License-Identifier: Apache-2.0 */
/* Generated by ~/srv/community/tools/bundle-css.mjs from index.css. */

/* ==== index.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI Design System — everything
   ------------------------------------------------------------
   Import order matters: tokens, then base, then signatures (which
   components compose), then components.

   Narrower entry points:
     ../tokens/index.css   variables only
     ./base.css            reset + document defaults
     ./signatures.css      the ten devices
   ============================================================ */


/* ==== ../tokens/index.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* Token layer only — no component styles, no reset.
   Import this alone if you want SEKAI's variables inside an app that
   already has its own component CSS (this is what hub should do to
   stop copy-pasting Pass's :root block). */


/* ==== ../tokens/primitives.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI Design System — Layer 0: Primitives
   ------------------------------------------------------------
   World-independent. Nothing here changes between world-system
   and world-night. Values are the union of the scales actually
   observed in nightcord/styles.css and sekai-pass/public/css.
   ============================================================ */

:root {
  /* ---------- Type families ----------
     sans  = content on a screen
     mono  = silkscreen on a chassis. Labels, IDs, sizes, timecodes,
             status, code. This split is the single strongest signature
             of both source designs — see DESIGN_SYSTEM.md §3.        */
  --sekai-font-sans:
    "Inter", "Noto Sans JP", "Segoe UI", system-ui, -apple-system, sans-serif;
  --sekai-font-mono:
    "JetBrains Mono", "Cascadia Code", "Fira Code", "Menlo", "Monaco",
    "Consolas", "Noto Sans Mono", monospace;

  /* ---------- Type scale ---------- */
  --sekai-text-micro: 10px; /* mono signatures, entity labels, scope tags */
  --sekai-text-meta: 11px; /* hints, privacy notes, scrub time */
  --sekai-text-label: 12px; /* form labels, badges, timestamps */
  --sekai-text-sm: 13px; /* subtitles, scope names, inline errors */
  --sekai-text-md: 14px; /* Pass body */
  --sekai-text-lg: 15px; /* Nightcord message body + composer */
  --sekai-text-xl: 18px; /* card / auth titles */
  --sekai-text-2xl: 20px;
  --sekai-text-3xl: 24px; /* modal headings */
  --sekai-text-4xl: 32px; /* avatar initials */

  --sekai-weight-normal: 400;
  --sekai-weight-medium: 500;
  --sekai-weight-semibold: 600;
  --sekai-weight-bold: 700;

  --sekai-leading-none: 1;
  --sekai-leading-tight: 1.4;
  --sekai-leading: 1.5;
  --sekai-leading-relaxed: 1.6;

  /* Tracking is how mono reads as silkscreen. Never apply wide
     tracking to sans body copy. */
  --sekai-tracking-tight: -0.01em;
  --sekai-tracking-none: 0;
  --sekai-tracking-wide: 0.5px;
  --sekai-tracking-wider: 1px;
  --sekai-tracking-widest: 2px;

  /* ---------- Spacing ---------- */
  --sekai-space-3xs: 2px;
  --sekai-space-2xs: 4px;
  --sekai-space-xs: 6px;
  --sekai-space-sm: 8px;
  --sekai-space-md: 12px;
  --sekai-space-lg: 16px;
  --sekai-space-xl: 20px;
  --sekai-space-2xl: 24px;
  --sekai-space-3xl: 32px;
  --sekai-space-4xl: 48px;

  /* ---------- Radius ---------- */
  --sekai-radius-xs: 4px; /* inputs, buttons, badges (system register) */
  --sekai-radius-sm: 6px; /* chips, transport keys, status pills */
  --sekai-radius-md: 8px; /* cards, code blocks */
  --sekai-radius-lg: 12px; /* composer, toasts, modal buttons */
  --sekai-radius-xl: 18px; /* entity avatars */
  --sekai-radius-2xl: 24px; /* full-screen modals */
  --sekai-radius-full: 9999px;

  /* ---------- Elevation ----------
     NOT an abstract importance scale. The system models one lit
     scene with a fixed source above and to the left (DESIGN_SYSTEM.md
     §0 principle 1), and a shadow answers a physical question:
     how big is this object, and how far off its backing plane?

     So pick by SIZE, not by importance. A 32px chip has almost no
     area to cast with and needs a tight, dark shadow to read as
     detached at all; a full panel needs a wide, soft one. Two
     elements of equal importance and different size take different
     shadows — that is correct, not an inconsistency.

     Hand-tuned off-ladder values are legitimate and are listed with
     their reasons in DESIGN_SYSTEM.md §4. */
  --sekai-elev-0: none;
  --sekai-elev-1: 0 2px 4px rgb(0 0 0 / 0.2);
  --sekai-elev-2: 0 4px 12px rgb(0 0 0 / 0.2);
  --sekai-elev-3: 0 8px 24px rgb(0 0 0 / 0.3);
  --sekai-elev-4: 0 20px 60px rgb(0 0 0 / 0.5); /* Pass container */
  --sekai-elev-modal: 0 25px 50px -12px rgb(0 0 0 / 0.5);
  --sekai-elev-inset: inset 0 2px 4px rgb(0 0 0 / 0.2);

  /* ---------- Motion ---------- */
  --sekai-dur-instant: 0.1s;
  --sekai-dur-fast: 0.15s;
  --sekai-dur: 0.2s;
  --sekai-dur-slow: 0.3s;
  --sekai-dur-slower: 0.4s;

  --sekai-ease: cubic-bezier(0.4, 0, 0.2, 1);
  --sekai-ease-out: cubic-bezier(0.2, 0.8, 0.2, 1); /* nightcord default */
  --sekai-ease-spring: cubic-bezier(0.16, 1, 0.3, 1); /* entrances */
  --sekai-ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* The canonical hover gesture: brighten the hairline, lift 1px. */
  --sekai-lift: translateY(-1px);
  --sekai-lift-strong: translateY(-2px);

  /* ---------- Neutral tints ----------
     Complete colors, not triplets. World-independent additive
     overlays. Use these when you need "a bit lighter / darker than
     whatever is underneath" rather than a named surface. */
  --sekai-tint-hover: rgb(255 255 255 / 0.05);
  --sekai-tint-active: rgb(255 255 255 / 0.08);
  --sekai-tint-raised: rgb(255 255 255 / 0.02);
  --sekai-tint-sunken: rgb(0 0 0 / 0.2);
  --sekai-tint-scrim: rgb(0 0 0 / 0.25);

  /* ---------- Z ladder ---------- */
  --sekai-z-base: 0;
  --sekai-z-raised: 10;
  --sekai-z-sticky: 100;
  --sekai-z-overlay: 999;
  --sekai-z-modal: 1000;
  --sekai-z-toast: 9999;
  --sekai-z-viewer: 99999;

  /* ---------- Layout constants ---------- */
  --sekai-sidebar-width: 280px;
  --sekai-bar-height: 48px; /* server header / chat header */
  --sekai-sticker-size: 120px;
}

@media screen and (max-width: 480px) {
  :root {
    --sekai-sticker-size: 80px;
  }
}


/* ==== ../tokens/contract.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI Design System — Layer 1: The semantic contract
   ------------------------------------------------------------
   Every world MUST define every token in this file. Component CSS
   is only ever allowed to reference these names — never a world
   file, never a raw hex.

   COLOR RULE (one rule, no exceptions):
     Palette tokens hold a SPACE-SEPARATED RGB TRIPLET and are
     always consumed as  rgb(var(--sekai-x) / <alpha>).
     There are no hex twins. This is deliberate: sekai-pass's own
     CSS_REFACTOR_PLAN.md documents --error-color drifting between
     `#e57373` and `229 115 115` because two spellings existed.
     One spelling cannot drift.

   Two companions to that rule:
     --sekai-*-a          a bare number, the world's default alpha
                          for that role (line, scrim).
     --sekai-hairline*    a ready-made `1px solid rgb(...)` shorthand,
                          because it appears ~50 times and every
                          hand-written copy is a chance to drift.
   ============================================================ */

:root {
  /* Fallback world = world-system. Attaching .world-system or
     .world-night to <html>/<body> overrides these wholesale. */

  /* ---------- Canvas ladder ----------
     One rule, both worlds: RELATIVE TO THE PLANE IT SITS ON, raised
     catches more light and is lighter; recessed catches less and is
     darker. See DESIGN_SYSTEM.md §0 principle 2.

       world-night   #50476a canvas (the desk)
                     #3f355b sidebar — a trough cut into it
                     #50476a .channel — a chip lying in that trough
                     #2b2633 attachment — a deeper well
       world-system  #0b0b0e canvas (an instrument face, dark room)
                     #17171c panel mounted on it, catching light
                     #111114 input — a well cut into the panel

     The two worlds differ in ambient level, not in direction.

     ONLY DIFFERENCES ARE PHYSICAL. A plane has no absolute height,
     the same way a position has no absolute potential energy. These
     values are not elevations; they are one chosen frame. The SAME
     value reads as raised or as recessed depending only on what is
     under it — #2b2633 over the scrim is a modal floating up, and
     over the #50476a canvas it is a well cut down. Both readings are
     correct simultaneously.

     Which is why the scrim is load-bearing: rgb(20 20 25 / .92)
     crushes the canvas to near-black and thereby RESETS THE ZERO, so
     the modal reads as raised without changing its own colour. Remove
     the scrim and that same modal becomes a pit. The scrim is
     structure, not atmosphere.

     Note on names: `-raised` / `-sunken` bake a direction into the
     name — an absolute claim that contradicts the paragraph above.
     Values are right; read the names as "second / third plane from
     the canvas", not as a direction. See DESIGN_SYSTEM.md §10.1. */
  --sekai-canvas: 11 11 14; /* page field */
  --sekai-surface: 23 23 28; /* default panel sitting on the canvas */
  --sekai-surface-raised: 32 32 37; /* a plane above --sekai-surface */
  --sekai-surface-sunken: 17 17 20; /* a well: inputs, code, tracks */
  --sekai-surface-hover: 42 42 48; /* solid hover fill */

  /* ---------- Lines ----------
     Three jobs, not one:
       line      hairline around cards, inputs, chips
       strong    the same hairline, emphasized (hover / focus-adjacent)
       divider   structural rules between layout regions (bar bottoms,
                 sidebar edges). world-system draws these with the same
                 grey as `line`; world-night draws them dark while its
                 `line` is white-at-low-alpha. Collapsing the two would
                 make one of the worlds wrong. */
  --sekai-line: 42 42 48;
  --sekai-line-a: 1;
  --sekai-line-strong: 61 61 70;
  --sekai-line-strong-a: 1;
  --sekai-divider: 42 42 48;
  --sekai-divider-a: 1;

  /* ---------- Foreground ---------- */
  --sekai-fg: 226 226 230;
  --sekai-fg-muted: 117 117 122;
  --sekai-fg-subtle: 90 90 96;
  --sekai-fg-on-accent: 26 26 30; /* text sitting ON an accent fill */

  /* ---------- Accent ----------
     Two accents, two jobs — this is the shared shape both source
     designs already had:
       accent      bright, interactive: focus rings, icons, CTAs
       accent-deep structural: the 3-4px strips, gradient caps      */
  --sekai-accent: 164 140 214;
  --sekai-accent-hover: 189 166 232;
  --sekai-accent-deep: 89 68 131;

  /* Which accent a SOLID fill uses, and what colour survives on top of
     it. These differ per world because the worlds' accents differ in
     lightness: world-system fills with its light accent and sets dark
     text (Pass's own `--button-color: #1a1a1e`); world-night fills with
     the deeper accent and sets white (nightcord's own .sekai-update-btn).
     Both land on legible contrast; neither is a compromise. */
  --sekai-accent-fill: 164 140 214;

  /* ---------- Signal & semantics ----------
     Identical across worlds on purpose: a system whose "danger"
     changes hue per theme is two systems. */
  --sekai-signal: 255 85 0; /* live / recording / AI. Never decorative. */
  --sekai-danger: 229 115 115;
  --sekai-success: 129 199 132;
  --sekai-warning: 250 166 26;
  --sekai-info: 56 189 248;

  /* ---------- Card surface ----------
     The plane a small object sits on when it is lying ON the canvas
     rather than cut into it: link cards, the audio player body.
     nightcord names this `--sekai-bg-card` (#2f273f) and gives it a
     second step for when the object is LIVE (#352e46).

     It is a real plane, not a rung of the canvas ladder — snapping it
     to the nearest rung flattens the two-step ramp, and because
     --surface-hover is a neutral grey-violet it also sends the live
     state toward "under the cursor" instead of "awake". */
  --sekai-surface-card: 28 28 33;
  --sekai-surface-card-live: 35 35 41;

  /* ---------- Identity tints ----------
     Eight colours for telling people apart, as a printed colour-coded
     tag: a bright field with dark ink of the same hue. That is what a
     cable marker or an equipment label looks like, and glancing across
     a roster is exactly the job they do.

     Deliberately NOT in the world files. A person has to stay the same
     colour in nightcord, in hub and in Pass, or the colour stops being
     an identity. These are the only palette tokens a world may not
     re-point.

     Generated, not picked: hue anchored on SEKAI's own 293°, eight
     even 45° steps, one lightness so every one is equally legible.
     Re-run scripts/derive-identity-tints.mjs to change or verify —
     it also prints the contrast audit and the neighbour separation. */
  --sekai-identity-1: 175 151 255; /* H 293° — the house hue */
  --sekai-identity-2: 230 131 203; /* H 338° */
  --sekai-identity-3: 251 129 125; /* H  23° */
  --sekai-identity-4: 232 150 41; /* H  68° */
  --sekai-identity-5: 172 180 46; /* H 113° */
  --sekai-identity-6: 64 199 134; /* H 158° */
  --sekai-identity-7: 0 197 212; /* H 203° */
  --sekai-identity-8: 83 177 255; /* H 248° */

  --sekai-identity-1-ink: 65 51 105;
  --sekai-identity-2-ink: 90 40 77;
  --sekai-identity-3-ink: 100 38 38;
  --sekai-identity-4-ink: 93 51 0;
  --sekai-identity-5-ink: 65 68 0;
  --sekai-identity-6-ink: 0 79 46;
  --sekai-identity-7-ink: 0 76 85;
  --sekai-identity-8-ink: 13 66 108;

  /* ---------- Scrim ---------- */
  --sekai-scrim: 20 20 25;
  --sekai-scrim-a: 0.92;

  /* ---------- Radius register ----------
     The one non-colour thing a world is allowed to re-point. Controls
     are 4px in the terminal register, 6px in the ambient one; panels
     agree at 8px. Components reference these, not the raw scale, so a
     world shifts its whole feel without touching component CSS. */
  --sekai-radius-control: var(--sekai-radius-xs);
  --sekai-radius-panel: var(--sekai-radius-md);

  /* ---------- Control voice ----------
     Whether buttons and tabs speak in the machine voice. Pass's
     buttons are UPPERCASE / 700 / 1px-tracked; nightcord's are
     sentence-case / 600. Same component, world-appropriate register —
     this is the token that carries that difference, so no component
     ever needs a world-specific rule. */
  --sekai-control-transform: uppercase;
  --sekai-control-tracking: var(--sekai-tracking-wider);
  --sekai-control-weight: var(--sekai-weight-bold);

  /* ---------- Derived shorthands ---------- */
  --sekai-hairline: 1px solid rgb(var(--sekai-line) / var(--sekai-line-a));
  --sekai-hairline-strong: 1px solid
    rgb(var(--sekai-line-strong) / var(--sekai-line-strong-a));
  --sekai-hairline-divider: 1px solid
    rgb(var(--sekai-divider) / var(--sekai-divider-a));
  --sekai-focus-ring: 0 0 0 2px rgb(var(--sekai-accent) / 0.15);
  --sekai-glow-accent: 0 4px 20px rgb(var(--sekai-accent) / 0.3);
  --sekai-glow-signal: 0 0 4px rgb(var(--sekai-signal) / 1);

  /* ---------- Device sub-theme ----------
     The walkman/DAW audio player is deliberately a FOREIGN OBJECT:
     a physical device sitting inside the UI, not a surface of it.
     It keeps its matte-black chassis in both worlds. Scoped to
     .sekai-device — see DESIGN_SYSTEM.md §6.7. */
  --sekai-device-chassis: 30 30 36; /* #1e1e24 */
  --sekai-device-panel: 35 35 41; /* #232329 */
  --sekai-device-screen: 10 10 12; /* #0a0a0c */
  --sekai-device-line: 51 51 51; /* #333 */
  --sekai-device-fg: 208 208 208;
  --sekai-device-fg-dim: 80 80 80;
  --sekai-device-key: 51 51 51;
  --sekai-device-key-hover: 68 68 68;
}


/* ==== ../tokens/world-system.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   World: SYSTEM  —  derived from sekai-pass
   ------------------------------------------------------------
   Register: terminal. Near-black field, low chroma, solid grey
   hairlines, tight radii, the mono voice used liberally.
   Use for: SEKAI Pass, hub, docs, dashboards, anything that
   should read as infrastructure.
   ============================================================ */

.world-system,
:root.world-system {
  /* Canvas — ascends lighter */
  --sekai-canvas: 11 11 14; /* #0b0b0e */
  --sekai-surface: 23 23 28; /* #17171c */
  --sekai-surface-raised: 32 32 37; /* #202025 */
  --sekai-surface-sunken: 17 17 20; /* #111114 */
  --sekai-surface-hover: 42 42 48; /* #2a2a30 */

  /* Lines — solid grey; divider is the same grey as the hairline */
  --sekai-line: 42 42 48; /* #2a2a30 */
  --sekai-line-a: 1;
  --sekai-line-strong: 61 61 70;
  --sekai-line-strong-a: 1;
  --sekai-divider: 42 42 48;
  --sekai-divider-a: 1;

  /* Foreground */
  --sekai-fg: 226 226 230; /* #e2e2e6 */
  --sekai-fg-muted: 117 117 122; /* #75757a */
  --sekai-fg-subtle: 90 90 96;
  --sekai-fg-on-accent: 26 26 30; /* #1a1a1e — dark text on light accent */

  /* Accent — Pass violet */
  --sekai-accent: 164 140 214; /* #a48cd6 */
  --sekai-accent-hover: 189 166 232; /* #bda6e8 */
  --sekai-accent-deep: 89 68 131; /* #594483 */
  --sekai-accent-fill: 164 140 214;

  /* Player micro-ramp — same relationship as world-night's, moved
     onto this world's near-black field: one step above --surface,
     one more when live. */
  --sekai-surface-card: 28 28 33; /* #1c1c21 */
  --sekai-surface-card-live: 35 35 41; /* #232329 */

  /* Scrim */
  --sekai-scrim: 20 20 25;
  --sekai-scrim-a: 0.92;

  /* Radius register: this world prefers the tight end of the scale. */
  --sekai-radius-control: var(--sekai-radius-xs); /* 4px */
  --sekai-radius-panel: var(--sekai-radius-md); /* 8px */

  /* Control voice: machine. */
  --sekai-control-transform: uppercase;
  --sekai-control-tracking: var(--sekai-tracking-wider);
  --sekai-control-weight: var(--sekai-weight-bold);
}


/* ==== ../tokens/world-night.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   World: NIGHT  —  derived from nightcord
   ------------------------------------------------------------
   Register: ambient. Violet field, mid chroma, white-at-low-alpha
   hairlines, softer radii, the mono voice reserved for meta.
   Use for: nightcord, 25ji-sagyo, stickers — anything the user
   sits inside for a long time.
   ============================================================ */

.world-night,
:root.world-night {
  /* Canvas — descends darker as it rises. Elevation is carried by
     shadow and hairline, not by fill lightness. */
  --sekai-canvas: 80 71 106; /* #50476a */
  --sekai-surface: 63 53 91; /* #3f355b — sidebar, composer */
  --sekai-surface-raised: 43 38 51; /* #2b2633 — modals, attachments */
  --sekai-surface-sunken: 30 27 36; /* #1e1b24 — inputs, wells */
  --sekai-surface-hover: 61 55 80; /* #3d3750 */

  /* Lines — hairlines are white at low alpha; structural dividers are
     the dark plum bar-rule. These genuinely differ here. */
  --sekai-line: 255 255 255;
  --sekai-line-a: 0.08;
  --sekai-line-strong: 255 255 255;
  --sekai-line-strong-a: 0.2;
  --sekai-divider: 43 38 51; /* #2b2633 */
  --sekai-divider-a: 1;

  /* Foreground */
  --sekai-fg: 255 255 255;
  --sekai-fg-muted: 189 189 189; /* #bdbdbd */
  --sekai-fg-subtle: 136 136 136; /* #888 */
  --sekai-fg-on-accent: 255 255 255; /* white text on the deep accent */

  /* Accent — nightcord violet. Same two roles as world-system:
     `accent` is the bright interactive one (#a78bfa), `accent-deep`
     is the structural strip colour (#7c6fac). */
  --sekai-accent: 167 139 250; /* #a78bfa */
  --sekai-accent-hover: 196 181 253; /* #c4b5fd */
  --sekai-accent-deep: 124 111 172; /* #7c6fac */
  --sekai-accent-fill: 124 111 172; /* fills use the deeper one */

  /* Player micro-ramp — nightcord's own hand-tuned pair. */
  --sekai-surface-card: 47 39 63; /* #2f273f */
  --sekai-surface-card-live: 53 46 70; /* #352e46 */

  /* Scrim */
  --sekai-scrim: 20 20 25;
  --sekai-scrim-a: 0.92;

  /* Radius register: this world prefers the softer end. */
  --sekai-radius-control: var(--sekai-radius-sm); /* 6px */
  --sekai-radius-panel: var(--sekai-radius-md); /* 8px */

  /* Control voice: human. The mono voice stays on meta and labels. */
  --sekai-control-transform: none;
  --sekai-control-tracking: var(--sekai-tracking-none);
  --sekai-control-weight: var(--sekai-weight-semibold);
}




/* ==== base.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI Design System — Base
   Reset, document defaults, focus, scrollbars, motion policy.
   Assumes the token layer is already loaded.
   ============================================================ */

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--sekai-font-sans);
  font-size: var(--sekai-text-md);
  line-height: var(--sekai-leading);
  background-color: rgb(var(--sekai-canvas));
  color: rgb(var(--sekai-fg));
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

img,
svg,
video,
canvas {
  display: block;
  max-width: 100%;
}

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

/* Keyboard focus only — pointer interaction keeps the surfaces clean.
   Every interactive component relies on this instead of shipping its
   own focus outline. */
:focus-visible {
  outline: 2px solid rgb(var(--sekai-accent));
  outline-offset: 2px;
}

::selection {
  background: rgb(var(--sekai-accent) / 0.35);
  color: rgb(var(--sekai-fg));
}

/* ---------- Scrollbars ----------
   Thin, accent-tinted, never a chrome-coloured slab. */
* {
  scrollbar-width: thin;
  scrollbar-color: rgb(var(--sekai-accent) / 0.3) transparent;
}

*::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

*::-webkit-scrollbar-track {
  background: transparent;
}

*::-webkit-scrollbar-thumb {
  background: rgb(var(--sekai-accent) / 0.3);
  border-radius: 3px;
}

*::-webkit-scrollbar-thumb:hover {
  background: rgb(var(--sekai-accent) / 0.5);
}

/* ---------- Motion policy ----------
   Every animation in this system is decorative reinforcement, never
   the only carrier of meaning, so all of it is safe to disable. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- Touch ----------
   Coarse pointers get a 44px minimum target and lose hover styling
   in favour of an :active press. */
@media (hover: none) and (pointer: coarse) {
  .sekai-btn,
  .sekai-icon-btn,
  .sekai-channel,
  .sekai-roster-item {
    min-height: 44px;
  }
}


/* ==== signatures.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI Design System — Signature devices
   ------------------------------------------------------------
   Ten small mechanisms lifted out of nightcord and sekai-pass.
   Individually they are a few lines of CSS; collectively they are
   what makes a surface read as SEKAI rather than as generic dark
   mode. Reach for one of these before inventing decoration.

   They are composable utilities — put them ON a component, don't
   fork the component to get them.

   Two of them are pure wayfinding and share a rule (DESIGN_SYSTEM.md
   §0 principle 3): a 90deg gradient runs along the reading axis and
   POINTS — cap, sheen, flow. Any other angle describes which way a
   surface faces and how it catches the light — glass at 100deg, the
   entity avatar at 135deg. Before adding a gradient, answer which of
   the two it is; the angle follows from the answer, not from taste.
   ============================================================ */

/* --- 1. Cap -------------------------------------------------
   A 3px accent gradient sealing the top edge of a panel. From the
   Pass container. Use once per screen, on the primary panel; a
   second cap on the same screen kills the first one's authority. */
.sekai-cap {
  position: relative;
  overflow: hidden;
}

.sekai-cap::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(
    90deg,
    rgb(var(--sekai-accent-deep)),
    rgb(var(--sekai-accent)),
    rgb(var(--sekai-accent-deep))
  );
}

/* --- 2. Grid ------------------------------------------------
   40px graph-paper field at 3% white. From the Pass body. This is
   the world-system page background; it reads as "instrument panel"
   and should stay under content, never inside a card. */
.sekai-grid {
  background-image:
    linear-gradient(rgb(255 255 255 / 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
}

/* --- 3. Signature -------------------------------------------
   The mono voice at its smallest: uppercase, widely tracked, dim.
   `SEKAI PASS // SYSTEM`, `EDITABLE DATA`, `STEREO`, entity labels.
   This single class carries most of the family resemblance between
   the two designs — see DESIGN_SYSTEM.md §2. */
.sekai-sig {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-micro);
  font-weight: var(--sekai-weight-semibold);
  letter-spacing: var(--sekai-tracking-widest);
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
  opacity: 0.6;
}

.sekai-sig--loud {
  opacity: 1;
  color: rgb(var(--sekai-accent));
}

/* A slightly larger sibling for form labels and column headers. */
.sekai-label {
  display: block;
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  letter-spacing: var(--sekai-tracking-wide);
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
}

/* --- 4. Rule ------------------------------------------------
   A hairline divider, optionally interrupted by a mono label. Set
   --sekai-rule-bg to whatever the rule sits on so the label knocks
   the line out cleanly. */
.sekai-rule {
  --sekai-rule-bg: rgb(var(--sekai-surface));
  height: 1px;
  border: 0;
  background: rgb(var(--sekai-divider) / var(--sekai-divider-a));
  margin: var(--sekai-space-3xl) 0;
  position: relative;
}

.sekai-rule[data-label]::after {
  content: attr(data-label);
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--sekai-rule-bg);
  padding: 0 var(--sekai-space-md);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-micro);
  letter-spacing: var(--sekai-tracking-wide);
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
  white-space: nowrap;
}

/* --- 5. Strip -----------------------------------------------
   A 3px bar down the left edge: a CHANGE BAR, the rule printed in a
   technical manual's margin to mark a block you should be able to
   find without reading. The colour says which kind.

   Left specifically, and only left: the left gutter is the one column
   the eye already returns to on every line, so scanning it is free.

   Every marked block in the system uses it — quoted code, blockquotes,
   replies, link cards, alerts, and the message that named you. Six
   different meanings, one job: findable at a glance. */
.sekai-strip {
  border-left: 3px solid rgb(var(--sekai-accent-deep));
  padding-left: var(--sekai-space-md);
}

.sekai-strip--accent {
  border-left-color: rgb(var(--sekai-accent));
}
.sekai-strip--signal {
  border-left-color: rgb(var(--sekai-signal));
}
.sekai-strip--danger {
  border-left-color: rgb(var(--sekai-danger));
}
.sekai-strip--success {
  border-left-color: rgb(var(--sekai-success));
}

/* --- 6. Glass -----------------------------------------------
   A near-flat gradient of white-on-nothing plus a hairline and a
   little blur. From the nightcord file card. It is what an
   attachment looks like when it floats over a chat rather than
   sitting in a panel. Keep the alphas low — this is glass, not
   frosting. */
.sekai-glass {
  background: linear-gradient(
    100deg,
    rgb(255 255 255 / 0.06),
    rgb(255 255 255 / 0.02)
  );
  border: var(--sekai-hairline);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.sekai-glass:hover {
  background: linear-gradient(
    100deg,
    rgb(255 255 255 / 0.1),
    rgb(255 255 255 / 0.04)
  );
}

/* Heavier variant for overlays that must stay readable over anything
   (toasts, floating menus). From the nightcord update toast. */
.sekai-glass--overlay {
  background: rgb(var(--sekai-surface-raised) / 0.85);
  border: 1px solid rgb(255 255 255 / 0.15);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 10px 40px rgb(0 0 0 / 0.5);
}

/* --- 7. Lift ------------------------------------------------
   THE hover gesture of this system: brighten the hairline, rise one
   pixel, settle back on press. Nothing scales up, nothing glows on
   hover alone. Apply to any card or button that is clickable. */
.sekai-lift {
  transition:
    transform var(--sekai-dur) var(--sekai-ease-out),
    border-color var(--sekai-dur) var(--sekai-ease-out),
    background var(--sekai-dur) var(--sekai-ease-out),
    box-shadow var(--sekai-dur) var(--sekai-ease-out);
}

.sekai-lift:hover {
  transform: var(--sekai-lift);
  border-color: rgb(var(--sekai-line-strong) / var(--sekai-line-strong-a));
}

.sekai-lift:active {
  transform: translateY(0);
}

/* --- 8. Sheen -----------------------------------------------
   A single diagonal highlight sweeping across on hover. From the
   nightcord "Sign in with SEKAI Pass" button. Reserve it for ONE
   element per screen — the primary identity action. It stops being
   special the moment two of them run. */
.sekai-sheen {
  position: relative;
  overflow: hidden;
}

.sekai-sheen::after {
  content: "";
  position: absolute;
  top: 0;
  left: -100%;
  width: 50%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent,
    rgb(255 255 255 / 0.1),
    transparent
  );
  transform: skewX(-20deg);
  transition: left 0.5s;
  pointer-events: none;
}

.sekai-sheen:hover::after {
  left: 150%;
  transition: left 0.7s ease-in-out;
}

/* --- 9. LED -------------------------------------------------
   A 6px status lamp that actually glows when lit. From the walkman
   transport. The ONLY place --sekai-signal is allowed to appear as
   a fill, alongside transport keys and AI mentions. */
.sekai-led {
  width: 6px;
  height: 6px;
  border-radius: var(--sekai-radius-full);
  background: rgb(var(--sekai-device-line));
  flex-shrink: 0;
  transition: background var(--sekai-dur), box-shadow var(--sekai-dur);
}

/* `.active` is nightcord's own state class and the SEKAI renderer
   already emits it; `.is-active` is the spelling for new code. Both
   are supported everywhere a state class appears in this system. */
.sekai-led.is-active,
.sekai-led.active {
  background: rgb(var(--sekai-signal));
  box-shadow: 0 0 4px rgb(var(--sekai-signal));
}

.sekai-led--accent.is-active,
.sekai-led--accent.active {
  background: rgb(var(--sekai-accent));
  box-shadow: 0 0 4px rgb(var(--sekai-accent));
}
.sekai-led--success.is-active,
.sekai-led--success.active {
  background: rgb(var(--sekai-success));
  box-shadow: 0 0 4px rgb(var(--sekai-success));
}

/* --- 10. Flow -----------------------------------------------
   A hairline with a packet of accent travelling along it. From the
   Pass authorize screen's user <-> app connection. Use it to depict
   a relationship between two entities, never as ambient decoration. */
.sekai-flow {
  flex: 1;
  height: 1px;
  background: rgb(var(--sekai-divider) / var(--sekai-divider-a));
  position: relative;
  overflow: visible;
}

.sekai-flow::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  transform: translateY(-50%);
  width: 100%;
  height: 3px;
  border-radius: 2px;
  opacity: 0.6;
  background: linear-gradient(
    90deg,
    transparent 0%,
    transparent 30%,
    rgb(var(--sekai-accent)) 50%,
    transparent 70%,
    transparent 100%
  );
  background-size: 200% 100%;
  animation: sekai-flow-x 2s infinite linear;
}

.sekai-flow--vertical {
  flex: none;
  width: 1px;
  height: 40px;
}

.sekai-flow--vertical::before {
  width: 3px;
  height: 100%;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 30%,
    rgb(var(--sekai-accent)) 50%,
    transparent 70%,
    transparent 100%
  );
  background-size: 100% 200%;
  animation: sekai-flow-y 2s infinite linear;
}

@keyframes sekai-flow-x {
  0% {
    background-position: 100% 0;
  }
  100% {
    background-position: -100% 0;
  }
}

@keyframes sekai-flow-y {
  0% {
    background-position: 0 100%;
  }
  100% {
    background-position: 0 -100%;
  }
}

/* --- Shared keyframes used across components ---------------- */
@keyframes sekai-spin {
  to {
    transform: rotate(360deg);
  }
}

@keyframes sekai-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes sekai-slide-up {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes sekai-pop-in {
  from {
    opacity: 0;
    transform: translateY(20px) scale(0.95);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes sekai-float {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-6px);
  }
}

@keyframes sekai-shimmer {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(100%);
  }
}

@keyframes sekai-blink {
  0%,
  50% {
    opacity: 1;
  }
  51%,
  100% {
    opacity: 0;
  }
}



/* ==== components/button.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Button
   ------------------------------------------------------------
   Note on width: sekai-pass styles the bare `button` element with
   `width: 100%`, then spends four separate rules (.btn-auto,
   .avatar-upload-btn, .app-card-actions button, and two
   `!important`s) clawing it back. Here the default is auto and
   full width is opt-in via --block. Same visual result on the
   auth forms, no escape hatches.
   ============================================================ */

.sekai-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sekai-space-sm);
  padding: 14px var(--sekai-space-lg);
  min-width: 96px;
  border: 1px solid transparent;
  border-radius: var(--sekai-radius-control);
  font-family: var(--sekai-font-sans);
  font-size: var(--sekai-text-md);
  font-weight: var(--sekai-control-weight);
  letter-spacing: var(--sekai-control-tracking);
  text-transform: var(--sekai-control-transform);
  line-height: var(--sekai-leading-none);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition:
    background var(--sekai-dur) var(--sekai-ease-out),
    border-color var(--sekai-dur) var(--sekai-ease-out),
    color var(--sekai-dur) var(--sekai-ease-out),
    transform var(--sekai-dur) var(--sekai-ease-out),
    box-shadow var(--sekai-dur) var(--sekai-ease-out);
}

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

.sekai-btn:disabled,
.sekai-btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.sekai-btn svg {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
}

/* ---------- Variants ---------- */

/* Primary — the only solid accent fill in the system. One per view. */
.sekai-btn--primary {
  background: rgb(var(--sekai-accent-fill));
  color: rgb(var(--sekai-fg-on-accent));
}

.sekai-btn--primary:hover:not(:disabled) {
  background: rgb(var(--sekai-accent-hover));
  transform: var(--sekai-lift);
  box-shadow: var(--sekai-glow-accent);
}

/* Secondary — the default for everything that isn't THE action. */
.sekai-btn--secondary {
  background: transparent;
  border-color: rgb(var(--sekai-line) / var(--sekai-line-a));
  color: rgb(var(--sekai-fg-muted));
}

.sekai-btn--secondary:hover:not(:disabled) {
  background: var(--sekai-tint-hover);
  border-color: rgb(var(--sekai-fg-muted));
  color: rgb(var(--sekai-fg));
  transform: var(--sekai-lift);
}

/* Ghost — toolbar and inline actions; no chrome until touched. */
.sekai-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: rgb(var(--sekai-fg-muted));
  min-width: 0;
}

.sekai-btn--ghost:hover:not(:disabled) {
  background: var(--sekai-tint-hover);
  color: rgb(var(--sekai-fg));
}

/* Danger — filled tint, not a solid red slab. */
.sekai-btn--danger {
  background: rgb(var(--sekai-danger) / 0.15);
  border-color: rgb(var(--sekai-danger) / 0.4);
  color: rgb(var(--sekai-danger));
}

.sekai-btn--danger:hover:not(:disabled) {
  background: rgb(var(--sekai-danger) / 0.25);
  border-color: rgb(var(--sekai-danger) / 0.8);
}

/* Danger outline — for destructive actions that are not the focus
   of the screen (revoke, delete inside a settings row). */
.sekai-btn--danger-outline {
  background: transparent;
  border-color: rgb(var(--sekai-danger) / 0.3);
  color: rgb(var(--sekai-danger) / 0.8);
}

.sekai-btn--danger-outline:hover:not(:disabled) {
  background: rgb(var(--sekai-danger) / 0.05);
  border-color: rgb(var(--sekai-danger) / 0.8);
  color: rgb(var(--sekai-danger));
  box-shadow: 0 4px 20px rgb(var(--sekai-danger) / 0.15);
}

/* Identity — "Continue with SEKAI Pass". The one button allowed to
   wear .sekai-sheen. Reads as a doorway, not as a CTA. */
.sekai-btn--identity {
  background: rgb(var(--sekai-surface-raised) / 0.6);
  border-color: rgb(var(--sekai-accent) / 0.3);
  color: rgb(var(--sekai-fg));
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition-duration: var(--sekai-dur-slow);
}

.sekai-btn--identity:hover:not(:disabled) {
  background: rgb(var(--sekai-accent) / 0.15);
  border-color: rgb(var(--sekai-accent));
  box-shadow: 0 4px 25px rgb(var(--sekai-accent) / 0.35);
}

.sekai-btn--identity svg {
  color: rgb(var(--sekai-accent));
  transition:
    transform var(--sekai-dur-slow) ease,
    color var(--sekai-dur-slow) ease,
    filter var(--sekai-dur-slow) ease;
}

.sekai-btn--identity:hover:not(:disabled) svg {
  transform: scale(1.1);
  color: rgb(var(--sekai-fg));
  filter: drop-shadow(0 0 6px rgb(255 255 255 / 0.6));
}

/* ---------- Sizes ---------- */
.sekai-btn--sm {
  padding: var(--sekai-space-sm) var(--sekai-space-md);
  font-size: var(--sekai-text-sm);
  min-width: 0;
}

.sekai-btn--lg {
  padding: var(--sekai-space-lg);
  font-size: var(--sekai-text-lg);
}

/* ---------- Layout modifiers ---------- */
.sekai-btn--block {
  display: flex;
  width: 100%;
}

/* ============================================================
   Icon button — round, chromeless, 32px. The composer actions,
   the message reply affordance, the toast dismiss.
   ============================================================ */

.sekai-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  border: none;
  border-radius: var(--sekai-radius-full);
  color: rgb(var(--sekai-fg-muted));
  font-size: var(--sekai-text-xl);
  cursor: pointer;
  transition:
    background var(--sekai-dur),
    color var(--sekai-dur),
    transform var(--sekai-dur) var(--sekai-ease);
}

.sekai-icon-btn:hover {
  background: rgb(var(--sekai-surface-hover));
  color: rgb(var(--sekai-fg));
}

.sekai-icon-btn svg {
  width: 20px;
  height: 20px;
}

/* Dismiss rotates rather than fading — the one playful gesture kept
   from the nightcord update toast. */
.sekai-icon-btn--dismiss:hover {
  background: rgb(255 255 255 / 0.1);
  transform: rotate(90deg);
}

.sekai-icon-btn--dismiss:active {
  transform: rotate(90deg) scale(0.9);
}

@media (hover: none) and (pointer: coarse) {
  .sekai-icon-btn:active {
    background: rgb(var(--sekai-surface-hover));
    transform: scale(0.95);
  }
}

/* ---------- Button groups ---------- */
.sekai-btn-row {
  display: flex;
  gap: var(--sekai-space-md);
  flex-wrap: wrap;
}

.sekai-btn-row--split {
  justify-content: space-between;
  align-items: center;
}

/* Two equal columns — the Approve / Deny pair on the authorize page. */
.sekai-btn-grid {
  display: grid;
  gap: var(--sekai-space-md);
  grid-template-columns: 1fr 1fr;
}

@media (max-width: 768px) {
  .sekai-btn-grid {
    grid-template-columns: 1fr;
  }

  .sekai-btn-row--split {
    flex-direction: column;
    align-items: stretch;
  }
}


/* ==== components/field.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Fields — label, input, textarea, checkbox, hint, error
   ------------------------------------------------------------
   The label is always the machine voice (mono, uppercase, dim);
   the value is always the human voice (sans). That contrast is
   what makes a SEKAI form look like an instrument rather than a
   web form, and it is the single rule to keep when adding a new
   control type.
   ============================================================ */

.sekai-field {
  margin-bottom: var(--sekai-space-2xl);
}

.sekai-field__label {
  display: block;
  margin-bottom: var(--sekai-space-sm);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  letter-spacing: var(--sekai-tracking-wide);
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
}

.sekai-input,
.sekai-textarea {
  display: block;
  width: 100%;
  padding: 14px var(--sekai-space-lg);
  background: rgb(var(--sekai-surface-sunken));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-control);
  color: rgb(var(--sekai-fg));
  font-family: var(--sekai-font-sans);
  font-size: var(--sekai-text-md);
  line-height: var(--sekai-leading);
  transition:
    border-color var(--sekai-dur),
    box-shadow var(--sekai-dur),
    background var(--sekai-dur);
}

.sekai-textarea {
  min-height: 80px;
  resize: vertical;
}

.sekai-input:focus,
.sekai-textarea:focus {
  outline: none;
  border-color: rgb(var(--sekai-accent));
  box-shadow: var(--sekai-focus-ring);
}

.sekai-input::placeholder,
.sekai-textarea::placeholder {
  color: rgb(var(--sekai-fg-subtle) / 0.7);
}

/* Disabled values switch to mono: a read-only field is machine data,
   not something you are being asked to write. */
.sekai-input:disabled,
.sekai-textarea:disabled {
  background: var(--sekai-tint-sunken);
  border-color: transparent;
  color: rgb(var(--sekai-fg-muted));
  font-family: var(--sekai-font-mono);
  cursor: not-allowed;
}

/* ---------- Validation ---------- */
.sekai-input--error,
.sekai-input[aria-invalid="true"] {
  border-color: rgb(var(--sekai-danger));
}

.sekai-input--error:focus,
.sekai-input[aria-invalid="true"]:focus {
  border-color: rgb(var(--sekai-danger));
  box-shadow: 0 0 0 2px rgb(var(--sekai-danger) / 0.15);
}

/* Live URL validation, straight from the Pass settings page. */
.sekai-input[type="url"]:invalid:not(:placeholder-shown) {
  border-color: rgb(var(--sekai-danger) / 0.5);
}

.sekai-input[type="url"]:valid:not(:placeholder-shown) {
  border-color: rgb(var(--sekai-success) / 0.3);
}

/* ---------- Helper text ---------- */
.sekai-hint {
  display: block;
  margin-top: var(--sekai-space-xs);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-meta);
  color: rgb(var(--sekai-fg-muted));
  opacity: 0.7;
}

.sekai-error-text {
  display: block;
  margin-top: var(--sekai-space-sm);
  min-height: 20px;
  font-size: var(--sekai-text-sm);
  font-weight: var(--sekai-weight-medium);
  color: rgb(var(--sekai-danger));
  opacity: 0;
  transition: opacity var(--sekai-dur);
}

.sekai-error-text.is-visible {
  opacity: 1;
}

/* ---------- Checkbox row ----------
   Escapes the label's uppercase/mono treatment on purpose: consent
   text is prose and must read as prose. */
.sekai-check {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sekai-space-sm);
  margin-bottom: var(--sekai-space-2xl);
  font-family: var(--sekai-font-sans);
  font-size: var(--sekai-text-sm);
  line-height: var(--sekai-leading);
  letter-spacing: normal;
  text-transform: none;
  color: rgb(var(--sekai-fg-muted));
  cursor: pointer;
}

.sekai-check input[type="checkbox"] {
  width: auto;
  margin: 0;
  accent-color: rgb(var(--sekai-accent));
  cursor: pointer;
}

.sekai-check a {
  color: rgb(var(--sekai-accent));
}

.sekai-check a:hover {
  text-decoration: underline;
}

/* ---------- Composite rows ---------- */

/* An input with a trailing action button (avatar URL + Upload). */
.sekai-input-row {
  display: flex;
  gap: var(--sekai-space-sm);
  align-items: stretch;
}

.sekai-input-row > .sekai-input {
  flex: 1;
  min-width: 0;
}

.sekai-input-row > .sekai-btn {
  flex-shrink: 0;
  white-space: nowrap;
  min-width: 0;
  padding-inline: var(--sekai-space-lg);
}

@media (max-width: 768px) {
  .sekai-input-row {
    flex-direction: column;
  }
}

/* A composer-style wrapper: the field IS the surface. Used by the
   nightcord message box, where the input has no chrome of its own. */
.sekai-input-shell {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sekai-space-md);
  padding: var(--sekai-space-md) var(--sekai-space-lg);
  background: rgb(var(--sekai-surface));
  border-radius: var(--sekai-radius-lg);
  overflow: visible;
}

.sekai-input-shell .sekai-input-bare {
  flex: 1;
  min-width: 0;
  padding: 0;
  background: transparent;
  border: none;
  outline: none;
  color: rgb(var(--sekai-fg));
  font-family: inherit;
  font-size: var(--sekai-text-lg);
  line-height: var(--sekai-leading);
  resize: none;
  overflow-y: auto;
  min-height: 22px;
  max-height: 140px;
}

.sekai-input-shell .sekai-input-bare::placeholder {
  color: rgb(var(--sekai-fg-subtle));
}


/* ==== components/card.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Card / Panel / Well
   ------------------------------------------------------------
   Three surfaces, three jobs:
     .sekai-card   a solid panel on the canvas
     .sekai-glass  (see signatures.css) a floating attachment
     .sekai-well   a recessed container for lists and scopes
   ============================================================ */

.sekai-card {
  background: rgb(var(--sekai-surface));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-panel);
  padding: var(--sekai-space-lg) var(--sekai-space-xl);
}

.sekai-card--flush {
  padding: 0;
}

.sekai-card--interactive {
  cursor: pointer;
  transition:
    transform var(--sekai-dur) var(--sekai-ease-out),
    border-color var(--sekai-dur) var(--sekai-ease-out),
    background var(--sekai-dur) var(--sekai-ease-out);
}

.sekai-card--interactive:hover {
  background: rgb(var(--sekai-surface-hover));
  border-color: rgb(var(--sekai-line-strong) / var(--sekai-line-strong-a));
  transform: var(--sekai-lift);
}

.sekai-card--danger {
  border-color: rgb(var(--sekai-danger) / 0.35);
}

.sekai-card--accent {
  border-color: rgb(var(--sekai-accent) / 0.4);
}

/* A dashed panel: something not yet filled in, or a drop target. */
.sekai-card--draft {
  background: var(--sekai-tint-raised);
  border-style: dashed;
}

/* ---------- Well ----------
   Sunken, gradient-floored, inner-shadowed. Holds rows that belong
   to each other: OAuth scopes, key/value metadata, uri lists. */
.sekai-well {
  background: linear-gradient(
    180deg,
    rgb(var(--sekai-surface-sunken) / 0.6) 0%,
    rgb(var(--sekai-canvas) / 0.4) 100%
  );
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-panel);
  box-shadow: var(--sekai-elev-inset);
  overflow: hidden;
}

.sekai-well__row {
  display: flex;
  align-items: flex-start;
  gap: var(--sekai-space-md);
  padding: var(--sekai-space-lg);
  border-bottom: 1px solid rgb(255 255 255 / 0.03);
  transition: background var(--sekai-dur);
}

.sekai-well__row:last-child {
  border-bottom: none;
}

.sekai-well__row:hover {
  background: var(--sekai-tint-raised);
}

/* ---------- Card header ---------- */
.sekai-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sekai-space-md);
  flex-wrap: wrap;
}

.sekai-card__title {
  margin: 0;
  font-size: var(--sekai-text-xl);
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-fg));
  /* Card titles are frequently user-supplied (OAuth app names).
     Never let one blow out the card. */
  overflow-wrap: anywhere;
}

.sekai-card__desc {
  margin-top: var(--sekai-space-sm);
  font-size: var(--sekai-text-sm);
  color: rgb(var(--sekai-fg-muted));
  overflow-wrap: anywhere;
}

/* ---------- Definition grid ----------
   Key/value metadata under a card header. Keys are machine voice. */
.sekai-meta {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--sekai-space-2xs) var(--sekai-space-md);
  margin-top: var(--sekai-space-md);
  font-size: var(--sekai-text-sm);
}

.sekai-meta dt {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  text-transform: uppercase;
  letter-spacing: var(--sekai-tracking-wide);
  color: rgb(var(--sekai-fg-muted));
  white-space: nowrap;
}

.sekai-meta dd {
  margin: 0;
  min-width: 0;
  color: rgb(var(--sekai-fg));
  overflow-wrap: anywhere;
}

.sekai-meta code {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  overflow-wrap: anywhere;
}

@media (max-width: 480px) {
  .sekai-meta {
    grid-template-columns: 1fr;
    gap: 2px;
  }
}

/* ---------- Secret value ----------
   A one-time-visible credential. `user-select: all` so the whole
   48-character string selects in one click — losing half a client
   secret to a sloppy drag is not an acceptable failure mode. */
.sekai-secret {
  display: block;
  margin-top: var(--sekai-space-md);
  padding: var(--sekai-space-md);
  background: rgb(var(--sekai-surface-sunken));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-sm);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-sm);
  user-select: all;
  overflow-wrap: anywhere;
}


/* ==== components/badge.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Badge / Tag / Pill
   ------------------------------------------------------------
   Always the machine voice. A badge that reads in sentence case
   is a chip, not a badge — use .sekai-chip for that.
   ============================================================ */

.sekai-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sekai-space-2xs);
  padding: 2px var(--sekai-space-xs);
  border: 1px solid rgb(255 255 255 / 0.02);
  border-radius: var(--sekai-radius-xs);
  background: rgb(255 255 255 / 0.07);
  color: rgb(var(--sekai-fg-muted));
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-micro);
  letter-spacing: var(--sekai-tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  flex: none;
}

/* Pill form — softer, used on card headers rather than inline. */
.sekai-badge--pill {
  padding: 2px 9px;
  border-radius: 10px;
  border-color: rgb(var(--sekai-line) / var(--sekai-line-a));
  background: rgb(255 255 255 / 0.05);
  font-size: var(--sekai-text-meta);
  text-transform: none;
  letter-spacing: normal;
}

/* Identity form — a bordered mono plate showing who you are signed
   in as. From the Pass authorize header. */
.sekai-badge--identity {
  padding: var(--sekai-space-xs) var(--sekai-space-md);
  background: rgb(var(--sekai-surface-raised));
  border-color: rgb(var(--sekai-line) / var(--sekai-line-a));
  border-radius: var(--sekai-radius-xs);
  font-size: var(--sekai-text-label);
  text-transform: none;
  letter-spacing: normal;
}

.sekai-badge--accent {
  background: rgb(var(--sekai-accent) / 0.15);
  border-color: rgb(var(--sekai-accent) / 0.3);
  color: rgb(var(--sekai-accent));
}

.sekai-badge--signal {
  background: rgb(var(--sekai-signal) / 0.15);
  border-color: rgb(var(--sekai-signal) / 0.3);
  color: rgb(var(--sekai-signal));
}

.sekai-badge--danger {
  background: rgb(var(--sekai-danger) / 0.15);
  border-color: rgb(var(--sekai-danger) / 0.3);
  color: rgb(var(--sekai-danger));
}

.sekai-badge--success {
  background: rgb(var(--sekai-success) / 0.15);
  border-color: rgb(var(--sekai-success) / 0.3);
  color: rgb(var(--sekai-success));
}

/* ---------- Chip ----------
   Sentence case, human voice, usually removable or clickable. */
.sekai-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  max-width: 100%;
  padding: 3px var(--sekai-space-sm);
  border-radius: var(--sekai-radius-xs);
  background: rgb(var(--sekai-surface-raised) / 0.4);
  color: rgb(var(--sekai-fg-muted));
  font-size: var(--sekai-text-sm);
  font-weight: var(--sekai-weight-medium);
  user-select: none;
  transition:
    background var(--sekai-dur) ease,
    color var(--sekai-dur) ease,
    border-color var(--sekai-dur) ease;
}

.sekai-chip--interactive {
  cursor: pointer;
}

.sekai-chip--interactive:hover {
  background: rgb(var(--sekai-surface-hover) / 0.5);
  color: rgb(var(--sekai-fg));
}


/* ==== components/alert.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Alert & Status
   ------------------------------------------------------------
   Alerts are square-edged and strip-marked, not rounded pastel
   boxes: a tinted field, a 3px left strip, mono text. The strip
   is doing the work — the tint is only there so the strip has
   something to belong to.
   ============================================================ */

.sekai-alert {
  --sekai-alert-color: var(--sekai-fg-muted);
  padding: var(--sekai-space-md) var(--sekai-space-lg);
  margin-bottom: var(--sekai-space-2xl);
  background: rgb(var(--sekai-alert-color) / 0.1);
  border-left: 3px solid rgb(var(--sekai-alert-color));
  color: rgb(var(--sekai-alert-color));
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-sm);
  line-height: var(--sekai-leading);
}

.sekai-alert--error {
  --sekai-alert-color: var(--sekai-danger);
}
.sekai-alert--success {
  --sekai-alert-color: var(--sekai-success);
}
.sekai-alert--warning {
  --sekai-alert-color: var(--sekai-warning);
}
.sekai-alert--info {
  --sekai-alert-color: var(--sekai-info);
}
.sekai-alert--accent {
  --sekai-alert-color: var(--sekai-accent);
}

/* ============================================================
   Status pill
   ------------------------------------------------------------
   A wide, centred, full-width state readout. From the Pass
   proof-of-work indicator: it occupies the space the eventual
   result will occupy, so nothing reflows when it resolves.
   ============================================================ */

.sekai-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sekai-space-sm);
  width: 100%;
  padding: var(--sekai-space-md) var(--sekai-space-xl);
  background: rgb(255 255 255 / 0.03);
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-sm);
  font-size: var(--sekai-text-md);
  color: rgb(var(--sekai-fg-muted));
  transition: all var(--sekai-dur) ease;
}

.sekai-status--success {
  border-color: rgb(var(--sekai-success) / 0.5);
  background: rgb(var(--sekai-success) / 0.05);
  color: rgb(var(--sekai-success));
}

.sekai-status--error {
  border-color: rgb(var(--sekai-danger) / 0.5);
  background: rgb(var(--sekai-danger) / 0.05);
  color: rgb(var(--sekai-danger));
  cursor: pointer;
}

.sekai-status--working {
  border-color: rgb(var(--sekai-accent) / 0.4);
}

/* ============================================================
   Toast
   ------------------------------------------------------------
   Bottom-right, heavy glass, springs in. Full-width on phones.
   ============================================================ */

.sekai-toast {
  position: fixed;
  right: var(--sekai-space-2xl);
  bottom: var(--sekai-space-2xl);
  z-index: var(--sekai-z-toast);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sekai-space-lg);
  min-width: 280px;
  padding: 14px var(--sekai-space-xl);
  border-radius: var(--sekai-radius-lg);
  color: rgb(var(--sekai-fg));
  animation: sekai-pop-in var(--sekai-dur-slower) var(--sekai-ease-spring);
}

.sekai-toast__content {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  font-size: var(--sekai-text-md);
  font-weight: var(--sekai-weight-medium);
}

.sekai-toast__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.sekai-toast__actions {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
}

@media screen and (max-width: 600px) {
  .sekai-toast {
    right: var(--sekai-space-md);
    bottom: var(--sekai-space-md);
    left: var(--sekai-space-md);
    min-width: 0;
    padding: var(--sekai-space-md) var(--sekai-space-lg);
    gap: var(--sekai-space-md);
  }

  .sekai-toast__content {
    font-size: var(--sekai-text-sm);
  }
}


/* ==== components/avatar.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Avatar
   ------------------------------------------------------------
   Two shapes with different meanings, kept from the sources:
     circle  a PERSON (nightcord roster + messages)
     squircle an ENTITY — an app, a service, a client
              (Pass authorize screen, 18px radius)
   Don't mix them; the shape is how you tell a user from an app
   at a glance.
   ============================================================ */

.sekai-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: var(--sekai-radius-full);
  overflow: hidden;
  background: rgb(var(--sekai-accent-deep));
  color: #fff;
  font-weight: var(--sekai-weight-semibold);
  font-size: var(--sekai-text-xl);
  line-height: 1;
  user-select: none;
}

.sekai-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  animation: sekai-fade-in var(--sekai-dur-slow) ease;
}

/* ---------- Sizes ---------- */
.sekai-avatar--xs {
  width: 24px;
  height: 24px;
  font-size: var(--sekai-text-label);
}
.sekai-avatar--sm {
  width: 28px;
  height: 28px;
  font-size: var(--sekai-text-md);
}
.sekai-avatar--lg {
  width: 64px;
  height: 64px;
  font-size: var(--sekai-text-3xl);
}
.sekai-avatar--xl {
  width: 80px;
  height: 80px;
  font-size: var(--sekai-text-4xl);
}

/* ---------- Entity shape ----------
   A rounded square with a soft gradient body. The glow colour says
   what kind of entity it is. */
.sekai-avatar--entity {
  border-radius: var(--sekai-radius-xl);
  background: linear-gradient(
    135deg,
    rgb(var(--sekai-surface-hover)),
    rgb(var(--sekai-surface-raised))
  );
  border: var(--sekai-hairline);
  color: rgb(var(--sekai-fg));
  font-weight: var(--sekai-weight-bold);
  box-shadow: var(--sekai-elev-3);
}

.sekai-avatar--entity.is-user {
  border-color: rgb(var(--sekai-info) / 0.2);
  box-shadow: 0 0 20px rgb(var(--sekai-info) / 0.1);
}

.sekai-avatar--entity.is-app {
  border-color: rgb(var(--sekai-accent) / 0.3);
  box-shadow: 0 0 20px rgb(var(--sekai-accent) / 0.15);
}

/* ---------- Upload preview ----------
   Ringed, with initials underneath that the image covers. */
.sekai-avatar--preview {
  width: 80px;
  height: 80px;
  background: rgb(var(--sekai-canvas));
  border: 2px solid rgb(var(--sekai-line) / var(--sekai-line-a));
  position: relative;
}

.sekai-avatar__initials {
  font-size: var(--sekai-text-4xl);
  font-weight: var(--sekai-weight-bold);
  color: rgb(var(--sekai-fg-muted));
  transition: opacity var(--sekai-dur) ease;
}

/* ---------- Identity tints ----------
   A printed colour-coded tag: bright field, dark ink of the same hue.
   The ink comes from the fill — as with buttons, the text colour
   follows the surface rather than the surface bending to suit a fixed
   text colour.

   Hash the user id into 1..8 rather than picking by hand; the colour
   is only an identity if the same person carries it everywhere. */
.sekai-avatar--t1,
.sekai-avatar--t2,
.sekai-avatar--t3,
.sekai-avatar--t4,
.sekai-avatar--t5,
.sekai-avatar--t6,
.sekai-avatar--t7,
.sekai-avatar--t8 {
  background: rgb(var(--sekai-identity));
  color: rgb(var(--sekai-identity-ink));
}

.sekai-avatar--t1 {
  --sekai-identity: var(--sekai-identity-1);
  --sekai-identity-ink: var(--sekai-identity-1-ink);
}
.sekai-avatar--t2 {
  --sekai-identity: var(--sekai-identity-2);
  --sekai-identity-ink: var(--sekai-identity-2-ink);
}
.sekai-avatar--t3 {
  --sekai-identity: var(--sekai-identity-3);
  --sekai-identity-ink: var(--sekai-identity-3-ink);
}
.sekai-avatar--t4 {
  --sekai-identity: var(--sekai-identity-4);
  --sekai-identity-ink: var(--sekai-identity-4-ink);
}
.sekai-avatar--t5 {
  --sekai-identity: var(--sekai-identity-5);
  --sekai-identity-ink: var(--sekai-identity-5-ink);
}
.sekai-avatar--t6 {
  --sekai-identity: var(--sekai-identity-6);
  --sekai-identity-ink: var(--sekai-identity-6-ink);
}
.sekai-avatar--t7 {
  --sekai-identity: var(--sekai-identity-7);
  --sekai-identity-ink: var(--sekai-identity-7-ink);
}
.sekai-avatar--t8 {
  --sekai-identity: var(--sekai-identity-8);
  --sekai-identity-ink: var(--sekai-identity-8-ink);
}

/* The same tag colour, used as a marker rather than as a face:
   presence dots, roster stripes, the caret on a user's own message. */
.sekai-identity-mark {
  background: rgb(var(--sekai-identity, var(--sekai-accent)));
}

/* ---------- Presence ---------- */
.sekai-presence {
  width: 8px;
  height: 8px;
  border-radius: var(--sekai-radius-full);
  flex-shrink: 0;
  background: rgb(var(--sekai-fg-subtle));
}

.sekai-presence--online {
  background: #43b581;
}

.sekai-presence--idle {
  background: rgb(var(--sekai-warning));
}

.sekai-presence--offline {
  background: #747f8d;
}


/* ==== components/feedback.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Feedback — spinner, progress, skeleton, empty state
   ============================================================ */

/* ---------- Spinner ---------- */
.sekai-spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 3px solid rgb(255 255 255 / 0.3);
  border-top-color: rgb(var(--sekai-accent));
  border-radius: var(--sekai-radius-full);
  animation: sekai-spin 1s ease-in-out infinite;
}

.sekai-spinner--sm {
  width: 18px;
  height: 18px;
  border-width: 2px;
  border-color: rgb(var(--sekai-accent) / 0.3);
  border-top-color: rgb(var(--sekai-accent));
  animation-duration: 0.8s;
  animation-timing-function: linear;
}

/* ---------- Progress ---------- */
.sekai-progress {
  display: flex;
  align-items: center;
  gap: 10px;
}

.sekai-progress__track {
  flex: 1;
  height: 4px;
  background: rgb(255 255 255 / 0.08);
  border-radius: 2px;
  overflow: hidden;
}

.sekai-progress__fill {
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: linear-gradient(
    90deg,
    rgb(var(--sekai-accent-deep)),
    rgb(var(--sekai-accent))
  );
  transition: width var(--sekai-dur-fast) ease;
}

.sekai-progress__value {
  min-width: 36px;
  text-align: right;
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-meta);
  color: rgb(var(--sekai-fg-muted));
}

/* ---------- Skeleton ----------
   An EMPTY BAY: a recessed slot, obviously shaped for the module that
   is going to sit in it, and completely still. Same rule as the
   oscilloscope — no signal, no invented motion. If the screen also
   needs to say "working on it", that is a different statement with
   its own vocabulary (LED / .sekai-status--working). */
.sekai-skeleton {
  background: rgb(var(--sekai-canvas) / 0.35);
  border-radius: var(--sekai-radius-sm);
  box-shadow: var(--sekai-elev-inset);
}

/* ---------- Empty state ---------- */
.sekai-empty {
  padding: var(--sekai-space-3xl) var(--sekai-space-lg);
  text-align: center;
  border: 1px dashed rgb(var(--sekai-line) / var(--sekai-line-a));
  border-radius: var(--sekai-radius-panel);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-empty p {
  margin: var(--sekai-space-xs) 0;
}

/* ---------- Inline loading text ---------- */
.sekai-loading-text {
  color: rgb(var(--sekai-fg-muted));
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-sm);
}


/* ==== components/modal.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Modal / Overlay
   ------------------------------------------------------------
   The nightcord name chooser, generalised. A blurred scrim, a
   large-radius panel that slides up, and a close affordance that
   stays out of the reading path.
   ============================================================ */

.sekai-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--sekai-z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sekai-space-lg);
  background: rgb(var(--sekai-scrim) / var(--sekai-scrim-a));
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  animation: sekai-fade-in var(--sekai-dur-slower) ease-out;
}

.sekai-overlay[hidden],
.sekai-overlay.is-hidden {
  display: none;
}

.sekai-modal {
  position: relative;
  width: 100%;
  max-width: 380px;
  padding: var(--sekai-space-4xl);
  background: rgb(var(--sekai-surface-raised));
  border: 1px solid rgb(255 255 255 / 0.08);
  border-radius: var(--sekai-radius-2xl);
  box-shadow: var(--sekai-elev-modal);
  text-align: center;
  animation: sekai-slide-up var(--sekai-dur-slower) var(--sekai-ease-out);
}

.sekai-modal__logo {
  display: flex;
  justify-content: center;
  margin-bottom: var(--sekai-space-2xl);
}

/* The mark breathes. This is the only ambient animation in the
   system that runs without user input — keep it to the logo. */
.sekai-modal__logo svg {
  filter: drop-shadow(0 0 10px rgb(var(--sekai-accent) / 0.4));
  animation: sekai-float 6s ease-in-out infinite;
}

.sekai-modal__title {
  margin: 0 0 var(--sekai-space-sm);
  font-size: var(--sekai-text-3xl);
  font-weight: var(--sekai-weight-bold);
  letter-spacing: var(--sekai-tracking-wide);
  color: rgb(var(--sekai-fg));
}

.sekai-modal__subtitle {
  margin: 0 0 var(--sekai-space-3xl);
  font-size: var(--sekai-text-md);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-modal__close {
  position: absolute;
  top: var(--sekai-space-lg);
  right: var(--sekai-space-lg);
}

/* ---------- Divider with a word in it ----------
   "OR" between a primary path and an alternative one. The gradient
   rules fade out at the edges so the word floats. */
.sekai-or {
  display: flex;
  align-items: center;
  width: 100%;
  margin: var(--sekai-space-2xl) 0;
  color: rgb(255 255 255 / 0.3);
  font-size: var(--sekai-text-label);
}

.sekai-or::before,
.sekai-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent,
    rgb(255 255 255 / 0.1),
    transparent
  );
}

.sekai-or > span {
  padding: 0 var(--sekai-space-md);
  font-weight: var(--sekai-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--sekai-tracking-wider);
}

@media screen and (max-width: 768px) {
  .sekai-modal {
    max-width: 90%;
    padding: 36px var(--sekai-space-2xl);
  }
}

@media screen and (max-width: 480px) {
  .sekai-modal {
    padding: var(--sekai-space-2xl) var(--sekai-space-xl);
  }

  .sekai-modal__title {
    font-size: var(--sekai-text-2xl);
  }
}

@media screen and (max-width: 768px) and (orientation: landscape) {
  .sekai-modal {
    max-height: 90vh;
    overflow-y: auto;
  }
}



/* ==== sekai/text.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI rich text
   ------------------------------------------------------------
   COMPATIBILITY CONTRACT: every class name in css/sekai/* is the
   name nightcord's sekai-renderer.js already emits, verbatim.
   That is deliberate — nightcord adopts this design system by
   deleting CSS, not by touching the renderer. Do not rename
   anything here without changing the renderer in lockstep.
   ============================================================ */

.sekai-text-node {
  line-height: var(--sekai-leading-relaxed);
}

.sekai-text-node strong {
  font-weight: var(--sekai-weight-bold);
  color: rgb(var(--sekai-fg));
}

/* Emphasis leans on the accent hue rather than on italics alone —
   at 15px on a violet field, pure italic is nearly invisible. */
.sekai-text-node em {
  font-style: italic;
  color: rgb(var(--sekai-accent-hover));
}

.sekai-text-node del {
  text-decoration: line-through;
  opacity: 0.7;
}

.sekai-text-node blockquote {
  margin: 10px 0;
  padding: var(--sekai-space-sm) var(--sekai-space-lg);
  border-left: 4px solid rgb(var(--sekai-accent-deep));
  border-radius: 0 var(--sekai-radius-md) var(--sekai-radius-md) 0;
  background: rgb(var(--sekai-accent-deep) / 0.08);
  color: rgb(var(--sekai-fg));
  font-style: normal;
}

/* ---------- Inline code ---------- */
.sekai-code-inline {
  padding: 2px var(--sekai-space-xs);
  border: 1px solid rgb(255 255 255 / 0.05);
  border-radius: 3px;
  background: rgb(var(--sekai-surface-raised));
  color: rgb(var(--sekai-warning));
  font-family: var(--sekai-font-mono);
  font-size: 0.9em;
}

/* ---------- Code block ---------- */
.sekai-code-block {
  display: block;
  margin: 10px 0;
  padding: var(--sekai-space-md) var(--sekai-space-lg);
  background: linear-gradient(
    145deg,
    rgb(var(--sekai-surface-raised) / 0.9),
    rgb(var(--sekai-surface-sunken) / 0.95)
  );
  color: rgb(var(--sekai-accent-hover));
  border: var(--sekai-hairline);
  border-left: 3px solid rgb(var(--sekai-accent-deep) / 0.5);
  border-radius: var(--sekai-radius-md);
  box-shadow: var(--sekai-elev-2);
  font-family: var(--sekai-font-mono);
  font-size: 0.9em;
  line-height: var(--sekai-leading-relaxed);
  white-space: pre;
  overflow-x: auto;
  tab-size: 2;
  transition: border-color var(--sekai-dur);
}

.sekai-code-block:hover {
  border-color: rgb(var(--sekai-accent) / 0.3);
  border-left-color: rgb(var(--sekai-accent));
}

/* Code with a filename header and a collapse toggle. */
.sekai-code-wrapper {
  display: block;
  margin: var(--sekai-space-xs) 0;
  max-width: 100%;
  border-radius: var(--sekai-radius-sm);
  background: rgb(var(--sekai-surface-sunken) / 0.65);
  overflow: hidden;
}

.sekai-code-header {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  padding: var(--sekai-space-2xs) 10px;
  background: rgb(0 0 0 / 0.25);
  border-bottom: 1px solid rgb(255 255 255 / 0.06);
  font-size: var(--sekai-text-label);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-code-name {
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-fg) / 0.85);
}

.sekai-code-lang {
  font-family: var(--sekai-font-mono);
  text-transform: lowercase;
  opacity: 0.7;
}

.sekai-code-toggle {
  margin-left: auto;
  padding: 1px var(--sekai-space-sm);
  background: transparent;
  border: 1px solid rgb(255 255 255 / 0.15);
  border-radius: var(--sekai-radius-xs);
  color: inherit;
  font-size: var(--sekai-text-meta);
  cursor: pointer;
}

.sekai-code-toggle:hover {
  background: rgb(255 255 255 / 0.08);
}

.sekai-code-wrapper .sekai-code-block {
  display: block;
  margin: 0;
  border-radius: 0;
}

.sekai-code-wrapper.collapsed .sekai-code-block {
  max-height: 4.5em;
  overflow: hidden;
  mask-image: linear-gradient(to bottom, #000 40%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 40%, transparent);
}

/* ---------- Spoiler ----------
   A strip of interference tape, not a blur. Blur leaks the shape of
   the text underneath; a solid diagonal-noise fill does not. Text is
   `color: transparent` AND unselectable until revealed. */
.sekai-spoiler {
  padding: 0 var(--sekai-space-2xs);
  margin: 0 2px;
  border: 1px solid rgb(var(--sekai-accent-deep) / 0.2);
  border-radius: var(--sekai-radius-xs);
  background-color: rgb(var(--sekai-surface-raised));
  background-image: repeating-linear-gradient(
    135deg,
    rgb(var(--sekai-surface-raised)),
    rgb(var(--sekai-surface-raised)) 6px,
    rgb(var(--sekai-surface-hover)) 6px,
    rgb(var(--sekai-surface-hover)) 12px
  );
  box-shadow: var(--sekai-elev-1);
  color: transparent;
  text-shadow: none;
  font-weight: var(--sekai-weight-medium);
  cursor: pointer;
  user-select: none;
  transition: all var(--sekai-dur) var(--sekai-ease-spring);
}

.sekai-spoiler:hover {
  border-color: rgb(var(--sekai-accent) / 0.6);
  box-shadow:
    0 2px 8px rgb(0 0 0 / 0.3),
    inset 0 0 0 1px rgb(255 255 255 / 0.05);
}

.sekai-spoiler.revealed,
.sekai-spoiler.is-revealed {
  padding: 0 2px;
  border-color: transparent;
  background-color: rgb(var(--sekai-accent-deep) / 0.15);
  background-image: none;
  box-shadow: none;
  color: inherit;
  cursor: text;
  user-select: text;
  animation: sekai-spoiler-reveal var(--sekai-dur-slow) ease-out;
}

@keyframes sekai-spoiler-reveal {
  0% {
    filter: blur(4px);
    opacity: 0.5;
  }
  100% {
    filter: blur(0);
    opacity: 1;
  }
}

/* On touch there is no hover, so don't half-reveal on tap-and-hold. */
@media (hover: none) and (pointer: coarse) {
  .sekai-spoiler:hover {
    border-color: rgb(var(--sekai-accent-deep) / 0.2);
    box-shadow: var(--sekai-elev-1);
  }
}

/* ---------- Re-covering ----------
   A single piece of tape is one-way, and that is correct: peeled tape
   does not re-adhere, and the irreversibility is honest about what
   revealing costs.

   But "content warning" is an abstraction, and it HAS the operation
   "cover it again" — someone walks up behind you and the risk is back.
   A metaphor that blocks an operation the thing really has is a cage
   (DESIGN_SYSTEM.md §0.0), so the metaphor has to be asked a harder
   question rather than abandoned.

   The harder question: physically you would never re-stick one strip.
   You would cover the whole surface again — shut the folder, close the
   lid. So the operation lives one level up, on a container, and it
   re-covers everything under it at once. Which is also the better
   interaction: in the situation that actually calls for this, you want
   everything hidden NOW, not to hunt each strip individually.

   Put .sekai-recover on any ancestor — a message, the whole log. */
.sekai-recover .sekai-spoiler.revealed,
.sekai-recover .sekai-spoiler.is-revealed {
  padding: 0 var(--sekai-space-2xs);
  border-color: rgb(var(--sekai-accent-deep) / 0.2);
  background-color: rgb(var(--sekai-surface-raised));
  background-image: repeating-linear-gradient(
    135deg,
    rgb(var(--sekai-surface-raised)),
    rgb(var(--sekai-surface-raised)) 6px,
    rgb(var(--sekai-surface-hover)) 6px,
    rgb(var(--sekai-surface-hover)) 12px
  );
  box-shadow: var(--sekai-elev-1);
  color: transparent;
  cursor: pointer;
  user-select: none;
  animation: none;
}

.sekai-recover .sekai-image-spoiler .sekai-image-content {
  filter: blur(18px);
  transform: scale(1.05);
}

/* ---------- Autolinked URLs ----------
   No underline at rest; a low-alpha bottom border instead, which
   thickens on hover. Keeps dense chat text from turning into a
   fence of underscores. */
.sekai-link-inline {
  color: rgb(var(--sekai-info));
  text-decoration: none;
  border-bottom: 1px solid rgb(var(--sekai-info) / 0.3);
  transition: all var(--sekai-dur);
}

.sekai-link-inline:hover {
  color: rgb(var(--sekai-info) / 0.85);
  border-bottom-color: rgb(var(--sekai-info));
  border-bottom-width: 1.5px;
}

/* ---------- Mentions ---------- */
.sekai-mention {
  display: inline-flex;
  align-items: center;
  padding: 0 var(--sekai-space-xs);
  margin: 0 1px;
  border-radius: var(--sekai-radius-xs);
  background: rgb(var(--sekai-accent-deep) / 0.28);
  color: rgb(var(--sekai-accent-hover));
  font-weight: var(--sekai-weight-semibold);
  font-size: 0.95em;
  white-space: nowrap;
  vertical-align: baseline;
  cursor: pointer;
  transition:
    background var(--sekai-dur-fast) ease,
    color var(--sekai-dur-fast) ease;
}

.sekai-mention:hover,
.sekai-mention:focus {
  background: rgb(var(--sekai-accent-deep) / 0.45);
  color: rgb(var(--sekai-fg));
  outline: none;
}

/* An AI participant is the one thing in a message stream that isn't
   a person. It gets the signal colour — the same orange as a
   recording LED — and nothing else does. */
.sekai-mention-ai {
  background: rgb(var(--sekai-signal) / 0.22);
  color: rgb(var(--sekai-signal) / 0.85);
}

.sekai-mention-ai:hover,
.sekai-mention-ai:focus {
  background: rgb(var(--sekai-signal) / 0.4);
  color: rgb(var(--sekai-fg));
}

.sekai-mention-all {
  background: rgb(var(--sekai-danger) / 0.25);
  color: rgb(var(--sekai-danger));
  font-weight: var(--sekai-weight-bold);
}

.sekai-mention-all:hover,
.sekai-mention-all:focus {
  background: rgb(var(--sekai-danger) / 0.4);
  color: rgb(var(--sekai-fg));
}

.sekai-mention-role {
  background: rgb(var(--sekai-info) / 0.22);
  color: rgb(var(--sekai-info));
}

/* ---------- Formatted runs ----------
   `Format:` produces one of two spans depending only on whether the
   author passed a colour: .sekai-colored-text if they did,
   .sekai-format-text if they didn't (sekai-renderer.js renderFormat).

   nightcord styles the first at weight 500 and never styles the
   second at all, so it inherits 400. Same node type, two weights,
   and the thing deciding is "did a colour happen to be supplied" —
   the weight ends up encoding a branch instead of a meaning.

   Both get 500 here. A Format run is a marked run whether or not it
   is coloured; the colour is a separate axis on top.
   (The other way to close this is dropping both to 400 and letting
   colour do all the marking. That reads thinner on the violet field,
   so it isn't the default — but it is a legitimate call.) */
.sekai-format-text,
.sekai-colored-text {
  font-weight: var(--sekai-weight-medium);
}

.sekai-colored-text {
  transition: text-shadow var(--sekai-dur);
}

.sekai-colored-text:hover {
  filter: brightness(1.1);
}

/* Truecolor = the author's exact value, unadjusted for contrast. The
   diamond marks it as raw so a washed-out result reads as intent. */
.sekai-truecolor {
  position: relative;
  font-weight: var(--sekai-weight-semibold);
}

.sekai-truecolor::before {
  content: "◆";
  margin-right: 2px;
  font-size: 0.7em;
  opacity: 0.3;
  vertical-align: super;
}

/* ---------- Streaming caret ----------
   The block cursor that trails a token-by-token AI reply. */
.sekai-streaming .sekai-text-node::after,
.sekai-format-text.sekai-streaming::after {
  content: "▋";
  margin-left: 2px;
  animation: sekai-blink 1s infinite;
}


/* ==== sekai/cards.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI cards — file, link, reply
   ------------------------------------------------------------
   Three attachment cards, three different construction methods,
   on purpose:
     file   glass    it floats over the conversation
     link   panel + strip  it points somewhere else
     reply  chip + strip   it points backwards in the same thread
   Class names match sekai-renderer.js verbatim.
   ============================================================ */

/* ---------- File card ---------- */
.sekai-file-card {
  display: flex;
  align-items: center;
  gap: 14px;
  width: fit-content;
  min-width: 300px;
  max-width: 450px;
  margin: var(--sekai-space-xs) 0;
  padding: var(--sekai-space-md);
  border-radius: var(--sekai-radius-md);
  background: linear-gradient(
    100deg,
    rgb(255 255 255 / 0.06),
    rgb(255 255 255 / 0.02)
  );
  border: var(--sekai-hairline);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  cursor: pointer;
  transition:
    transform var(--sekai-dur),
    background var(--sekai-dur),
    border-color var(--sekai-dur);
}

.sekai-file-card:hover {
  background: linear-gradient(
    100deg,
    rgb(255 255 255 / 0.1),
    rgb(255 255 255 / 0.04)
  );
  border-color: rgb(var(--sekai-line-strong) / var(--sekai-line-strong-a));
  transform: var(--sekai-lift);
}

.sekai-file-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  border-radius: var(--sekai-radius-md);
  background: rgb(var(--sekai-accent) / 0.15);
  color: rgb(var(--sekai-accent));
}

.sekai-file-details {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  overflow: hidden;
}

.sekai-file-name {
  font-size: 0.95em;
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-fg));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Byte counts are machine facts. Mono. */
.sekai-file-meta {
  margin-top: 2px;
  font-family: var(--sekai-font-mono);
  font-size: 0.75em;
  color: rgb(var(--sekai-fg-muted));
}

.sekai-file-action {
  display: flex;
  padding: var(--sekai-space-2xs);
  color: rgb(var(--sekai-fg-muted));
  opacity: 0.6;
  transition: opacity var(--sekai-dur);
}

.sekai-file-action:hover {
  opacity: 1;
  color: rgb(var(--sekai-fg));
}

/* ---------- Link card ---------- */
.sekai-link-card {
  display: flex;
  align-items: stretch;
  max-width: 480px;
  margin: var(--sekai-space-xs) 0;
  border-radius: var(--sekai-radius-sm);
  background: rgb(var(--sekai-surface-card) / 0.9);
  overflow: hidden;
  text-decoration: none;
  transition: background var(--sekai-dur);
}

.sekai-link-card:hover {
  background: rgb(var(--sekai-surface-hover) / 0.85);
}

/* The strip is the whole left edge, not a border — it reads as a
   spine the card hangs from. */
.sekai-link-accent {
  width: 4px;
  flex-shrink: 0;
  background: rgb(var(--sekai-accent-deep));
}

.sekai-link-content {
  flex: 1;
  padding: 10px var(--sekai-space-md);
  overflow: hidden;
}

.sekai-link-site {
  margin-bottom: 2px;
  font-size: 0.75em;
  letter-spacing: var(--sekai-tracking-wide);
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
}

.sekai-link-title {
  margin-bottom: var(--sekai-space-2xs);
  font-size: 0.95em;
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-accent-hover));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sekai-link-desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 0.85em;
  color: rgb(var(--sekai-fg-subtle));
}

/* The full URL, dimmest of all, in mono — you check it, you don't
   read it. */
.sekai-link-url {
  margin-top: 2px;
  font-family: var(--sekai-font-mono);
  font-size: 0.75em;
  color: rgb(var(--sekai-fg-subtle) / 0.7);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sekai-link-arrow {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--sekai-space-md);
  color: rgb(255 255 255 / 0.2);
  font-size: 1.2em;
  font-weight: 300;
}

/* ---------- Reply chip ---------- */
.sekai-reply-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  max-width: 100%;
  margin: 0 var(--sekai-space-2xs) 2px 0;
  padding: 3px var(--sekai-space-sm) 3px var(--sekai-space-xs);
  border-left: 3px solid rgb(var(--sekai-accent-deep));
  border-radius: var(--sekai-radius-xs);
  background: rgb(var(--sekai-surface-raised) / 0.4);
  color: rgb(var(--sekai-fg-muted));
  font-size: 0.85em;
  cursor: pointer;
  user-select: none;
  transition: all var(--sekai-dur) ease;
}

.sekai-reply-chip:hover {
  background: rgb(var(--sekai-surface-hover) / 0.5);
  border-left-color: rgb(var(--sekai-accent));
  color: rgb(var(--sekai-fg));
}

.sekai-reply-accent {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  color: rgb(var(--sekai-accent-deep));
  opacity: 0.8;
}

.sekai-reply-chip:hover .sekai-reply-accent {
  color: rgb(var(--sekai-accent));
  opacity: 1;
}

.sekai-reply-content {
  flex: 1;
  font-weight: var(--sekai-weight-medium);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Error placeholder ---------- */
.sekai-error-placeholder {
  padding: var(--sekai-space-xl);
  text-align: center;
  color: rgb(var(--sekai-danger));
}

.sekai-error-icon {
  display: block;
  margin-bottom: var(--sekai-space-sm);
  font-size: var(--sekai-text-3xl);
}

.sekai-error-text {
  font-size: var(--sekai-text-sm);
  font-style: italic;
}

/* Attachment cards go full-bleed on phones — a 300px min-width card
   in a 360px viewport leaves no margin to read against. */
@media screen and (max-width: 600px) {
  .sekai-file-card,
  .sekai-link-card {
    min-width: 100%;
    max-width: 100%;
  }
}


/* ==== sekai/media.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI media — images, stickers
   Class names match sekai-renderer.js verbatim.
   ============================================================ */

/* ---------- Image ---------- */
.sekai-image-container {
  display: inline-block;
  position: relative;
  max-width: 100%;
  margin: var(--sekai-space-xs) 0;
  border-radius: var(--sekai-radius-sm);
  background: rgb(0 0 0 / 0.1);
  overflow: hidden;
}

/* Reserve the large size while loading so the message doesn't jump
   when the natural width arrives. */
.sekai-image-container.loading {
  max-width: 400px;
}

.sekai-image-container.loading::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    rgb(var(--sekai-surface-raised)) 25%,
    rgb(var(--sekai-surface-hover)) 50%,
    rgb(var(--sekai-surface-raised)) 75%
  );
  background-size: 200% 100%;
  animation: sekai-shimmer 1.5s infinite;
}

.sekai-image-content {
  display: block;
  max-width: 100%;
  max-height: 400px;
  transition: opacity var(--sekai-dur-slow);
}

/* The caption is hidden until hover — it's attribution, not content. */
.sekai-image-caption {
  position: absolute;
  inset: auto 0 0 0;
  padding: var(--sekai-space-2xs) var(--sekai-space-sm);
  background: rgb(0 0 0 / 0.6);
  color: #fff;
  font-size: 0.85em;
  transform: translateY(100%);
  transition: transform var(--sekai-dur);
}

.sekai-image-container:hover .sekai-image-caption {
  transform: translateY(0);
}

.sekai-image-container.sekai-image-large {
  max-width: 400px;
}

.sekai-image-container.sekai-image-small {
  max-width: 200px;
}

/* ---------- Image spoiler ----------
   Unlike the text spoiler, blur IS correct here: the point is to
   hide the subject while keeping the composition visible enough to
   decide whether to look. Scale 1.05 stops the blur bleeding a
   transparent edge. */
.sekai-image-spoiler {
  position: relative;
  overflow: hidden;
}

.sekai-image-spoiler:not(.revealed) .sekai-image-content {
  filter: blur(18px);
  transform: scale(1.05);
  cursor: pointer;
}

.sekai-image-spoiler:not(.revealed)::after {
  content: "点击显示敏感内容";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--sekai-tint-scrim);
  border-radius: inherit;
  color: #fff;
  font-size: var(--sekai-text-label);
  pointer-events: none;
}

/* Override the default copy per locale without touching CSS. */
.sekai-image-spoiler[data-reveal-label]:not(.revealed)::after {
  content: attr(data-reveal-label);
}

.sekai-image-spoiler.revealed .sekai-image-content {
  filter: none;
  transform: none;
}

/* ---------- Stickers ---------- */
.sekai-sticker {
  vertical-align: middle;
  transition: transform var(--sekai-dur);
}

.sekai-sticker-inline {
  display: inline-block;
  height: 60px;
  width: auto;
  margin: 0 var(--sekai-space-2xs);
}

.sekai-sticker-single {
  display: block;
  max-width: var(--sekai-sticker-size);
  max-height: var(--sekai-sticker-size);
  margin: var(--sekai-space-2xs) 0;
  border-radius: var(--sekai-radius-sm);
}

/* 1.02, not 1.1. A sticker that leaps is a sticker that shoves the
   line height around. */
.sekai-sticker-single:hover {
  transform: scale(1.02);
}

.sekai-sticker-loading {
  display: inline-block;
  width: var(--sekai-sticker-size);
  height: var(--sekai-sticker-size);
  background: rgb(var(--sekai-surface-hover));
  vertical-align: middle;
}

.sekai-sticker-inline.sekai-sticker-loading {
  width: 60px;
  height: 60px;
}

.sekai-sticker-broken {
  display: inline-block;
  padding: 0 var(--sekai-space-xs);
  color: rgb(var(--sekai-fg-muted));
  font-style: italic;
}

@media screen and (max-width: 600px) {
  .sekai-sticker-inline,
  .sekai-sticker-inline.sekai-sticker-loading {
    height: 48px;
  }
}

/* ---------- Lightbox ---------- */
.sekai-image-viewer {
  position: fixed;
  inset: 0;
  z-index: var(--sekai-z-viewer);
  display: none;
  align-items: center;
  justify-content: center;
}

.sekai-image-viewer.active {
  display: flex;
}

.sekai-viewer-backdrop {
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.9);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}


/* ==== sekai/audio-device.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI audio player — the device
   ------------------------------------------------------------
   Walkman x DAW. Three modules butted together with no gaps:
   a console fader, a black screen, and a column of transport
   keys.

   This is the system's one FOREIGN OBJECT. It does not use the
   canvas ladder, it does not take the world's accent, and it does
   not soften its corners to match. It reads as a piece of hardware
   someone set down inside the interface — which is exactly why it
   works in both worlds unchanged. Everything it needs lives in the
   --sekai-device-* tokens plus --sekai-signal.

   If you are tempted to "harmonise" this with the surrounding UI:
   don't. The contrast is the design.
   ============================================================ */

.sekai-audio-player {
  display: flex;
  gap: 0;
  width: fit-content;
  min-width: 400px;
  height: 96px;
  margin: var(--sekai-space-md) 0;
  background-color: rgb(var(--sekai-device-chassis));
  border: 1px solid rgb(var(--sekai-device-line));
  border-radius: var(--sekai-radius-sm);
  box-shadow: 0 4px 6px rgb(0 0 0 / 0.1);
  font-family: var(--sekai-font-mono);
  overflow: hidden;
  user-select: none;
}

.sekai-audio-player > div {
  border-right: 1px solid rgb(var(--sekai-device-line));
}

.sekai-audio-player > div:last-child {
  border-right: none;
}

/* ---------- 1. Console fader ---------- */
.sekai-console-module {
  position: relative;
  width: 48px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--sekai-space-sm) 0;
  background: rgb(var(--sekai-device-panel));
}

.sekai-fader-scale {
  position: absolute;
  top: 13px;
  bottom: 13px;
  left: var(--sekai-space-sm);
  width: 4px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
}

.sekai-fader-tick {
  width: 100%;
  height: 1px;
  background: #444;
}

.sekai-fader-tick.major {
  width: 200%;
  background: #666;
}

.sekai-fader-track {
  position: relative;
  width: 4px;
  height: 70px;
  background: #111;
  border-radius: 2px;
}

/* `top` is set by JS. Flat cap with an indent line, no bevel. */
.sekai-fader-cap {
  position: absolute;
  left: 50%;
  z-index: var(--sekai-z-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 12px;
  background: #888;
  border-radius: 1px;
  transform: translateX(-50%) translateY(-50%);
  cursor: grab;
}

.sekai-fader-cap::after {
  content: "";
  width: 100%;
  height: 1px;
  background: #222;
}

.sekai-fader-cap:active {
  background: #ccc;
  cursor: grabbing;
}

/* ---------- 2. Screen ---------- */
.sekai-audio-content {
  flex: 1;
  min-width: 240px;
  position: relative;
  display: flex;
  flex-direction: column;
  background: rgb(var(--sekai-device-screen));
}

.sekai-screen-meta {
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sekai-space-sm);
  padding: 0 var(--sekai-space-sm);
  border-bottom: 1px solid #222;
  font-size: var(--sekai-text-micro);
  color: rgb(var(--sekai-device-fg-dim));
  overflow: hidden;
}

.sekai-status-indicator {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-xs);
  flex-shrink: 0;
}

.sekai-status-text {
  font-size: 9px;
  letter-spacing: var(--sekai-tracking-wider);
}

.sekai-track-info {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--sekai-space-xs);
  overflow: hidden;
}

.sekai-track-title {
  flex-shrink: 1;
  font-size: var(--sekai-text-meta);
  font-weight: var(--sekai-weight-semibold);
  color: #eee;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Shrinks twice as fast as the title: when space runs out the artist
   goes first. */
.sekai-track-artist {
  flex-shrink: 2;
  font-size: 9px;
  color: rgb(var(--sekai-device-fg-dim));
  opacity: 0.7;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sekai-track-artist::before {
  content: "- ";
}

.sekai-viz-area {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Tape reels sit at 20% opacity behind the waveform — a mechanism
   you notice on the second look, not the first. */
.sekai-tape-mechanism {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 40px;
  opacity: 0.2;
  pointer-events: none;
}

.sekai-reel {
  position: relative;
  width: 48px;
  height: 48px;
  border: 4px solid #444;
  border-radius: var(--sekai-radius-full);
}

.sekai-reel::before,
.sekai-reel::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 2px;
  background: #444;
}

.sekai-reel::after {
  transform: rotate(90deg);
}

.sekai-audio-player.playing .sekai-reel {
  animation: sekai-reel-spin 4s linear infinite;
}

@keyframes sekai-reel-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

/* DAW-style bars: grounded at the bottom, scaled from the origin. */
.sekai-audio-visualizer-bars {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 30px;
}

.sekai-viz-bar {
  width: 3px;
  height: 100%;
  background: #333;
  transform: scaleY(0.1);
  transform-origin: bottom;
  transition: transform var(--sekai-dur-instant);
}

.sekai-audio-player.playing .sekai-viz-bar {
  background: rgb(var(--sekai-device-fg));
}

/* ---------- Scrubber ---------- */
.sekai-tape-scrubber {
  position: relative;
  height: 16px;
  display: flex;
  align-items: center;
  background: #151515;
  border-top: 1px solid #222;
  cursor: pointer;
  transition: height var(--sekai-dur-instant) ease;
}

/* Grows on hover so the hit target follows the eye. */
.sekai-tape-scrubber:hover {
  height: 18px;
}

.sekai-tape-scrubber:active {
  cursor: grabbing;
}

.sekai-scrub-buffered {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
  width: 0;
  height: 100%;
  background: rgb(255 255 255 / 0.08);
  transition:
    width var(--sekai-dur-slow) ease,
    opacity 0.5s ease;
}

.sekai-scrub-progress {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: 0;
  height: 100%;
  background: rgb(255 255 255 / 0.05);
  border-right: 1px solid rgb(var(--sekai-signal));
  transition: background var(--sekai-dur-instant) ease;
}

.sekai-tape-scrubber:hover .sekai-scrub-progress {
  background: rgb(255 255 255 / 0.08);
}

/* Playhead: a triangle riding the edit line, as in an editor
   timeline. */
.sekai-scrub-progress::after {
  content: "";
  position: absolute;
  right: -3px;
  top: 0;
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
  border-top: 4px solid rgb(var(--sekai-signal));
  transition: border-top-width var(--sekai-dur-instant) ease;
}

.sekai-tape-scrubber:hover .sekai-scrub-progress::after {
  border-top-width: 6px;
}

.sekai-scrub-time {
  position: absolute;
  right: var(--sekai-space-xs);
  z-index: 3;
  font-size: var(--sekai-text-micro);
  color: rgb(var(--sekai-device-fg));
  pointer-events: none;
}

/* ---------- 3. Transport keys ---------- */
.sekai-walkman-transport {
  width: 50px;
  display: flex;
  flex-direction: column;
  gap: var(--sekai-space-2xs);
  padding: var(--sekai-space-xs);
  background: rgb(var(--sekai-device-panel));
}

.sekai-transport-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgb(var(--sekai-device-key));
  border: none;
  border-radius: 2px;
  color: #888;
  font-size: var(--sekai-text-label);
  cursor: pointer;
  transition: all var(--sekai-dur-instant);
}

.sekai-transport-btn:hover {
  background: rgb(var(--sekai-device-key-hover));
  color: #fff;
}

/* Latched down: the key stays lit and sunken, like a mechanical
   transport key that clicks into place. */
.sekai-transport-btn.active,
.sekai-transport-btn.is-active {
  background: rgb(var(--sekai-signal));
  color: #000;
  box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.2);
}

.sekai-transport-btn svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}

@media screen and (max-width: 600px) {
  .sekai-audio-player {
    min-width: 100%;
    max-width: 100%;
  }
}


/* ==== sekai/audio-simple.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   SEKAI audio player — simple
   ------------------------------------------------------------
   The device's quiet sibling: one DAW track embedded in the
   conversation. Unlike .sekai-audio-player this one DOES belong
   to the surrounding world — it takes the world's surface and
   accent — and keeps only the signal LED from the hardware.

   Use the device for a shared listening moment; use this for an
   attached voice note.
   ============================================================ */

.sekai-audio-player-simple {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 0;
  width: fit-content;
  min-width: 320px;
  max-width: 440px;
  height: 52px;
  margin: var(--sekai-space-sm) 0;
  background-color: rgb(var(--sekai-surface-card));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-md);
  /* Deliberately off the elevation scale, at half elev-1's alpha.
     This player sits IN the message flow rather than floating over
     it; at 0.2 it detaches and starts reading as a modal chip. */
  box-shadow: 0 2px 4px rgb(0 0 0 / 0.1);
  overflow: hidden;
  user-select: none;
  transition:
    transform var(--sekai-dur) ease,
    box-shadow var(--sekai-dur) ease,
    border-color var(--sekai-dur);
}

.sekai-audio-player-simple:hover {
  border-color: rgb(var(--sekai-accent) / 0.3);
  box-shadow: 0 4px 12px rgb(0 0 0 / 0.15); /* likewise: half-weight */
  transform: var(--sekai-lift);
}

/* Live = one step UP the player's own micro-ramp. Reaching for
   --surface-hover here would push it toward neutral grey-violet,
   i.e. away from "warmer, awake" and toward "under the cursor". */
.sekai-audio-player-simple.playing {
  border-color: rgb(var(--sekai-accent) / 0.5);
  background-color: rgb(var(--sekai-surface-card-live));
}

/* ---------- Play key ----------
   The one place in this system with a tactile inset highlight: it
   has to feel like a key, because it is the only control. */
.sekai-audio-control {
  flex-shrink: 0;
  width: 52px;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.03) 0%,
    rgb(255 255 255 / 0) 100%
  );
  border: none;
  border-right: 1px solid rgb(255 255 255 / 0.05);
  border-radius: var(--sekai-radius-md) 0 0 var(--sekai-radius-md);
  color: rgb(var(--sekai-fg-muted));
  cursor: pointer;
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.08),
    0 2px 4px rgb(0 0 0 / 0.2);
  transition: all var(--sekai-dur-instant) ease-out;
}

.sekai-audio-control:hover {
  background: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.08) 0%,
    rgb(255 255 255 / 0.02) 100%
  );
  color: rgb(var(--sekai-fg));
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.12),
    0 3px 6px rgb(0 0 0 / 0.25);
  transform: translateY(-1px);
}

/* Presses IN, not down-and-out: the highlight inverts to an inset
   shadow and the key drops a pixel. */
.sekai-audio-control:active {
  background: rgb(0 0 0 / 0.15);
  box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.2);
  border-right-color: transparent;
  transform: translateY(1px);
}

.sekai-audio-player-simple.playing .sekai-audio-control {
  color: rgb(var(--sekai-accent));
}

.sekai-audio-icon-span {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-bottom: var(--sekai-space-2xs);
}

.sekai-audio-control svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.3));
}

/* ---------- Indicator ----------
   Recessed when off, glowing signal-orange when playing. */
.sekai-audio-indicator {
  width: 4px;
  height: 4px;
  border-radius: var(--sekai-radius-full);
  background-color: rgb(255 255 255 / 0.2);
  box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.3);
  transition: all var(--sekai-dur-slow) var(--sekai-ease);
}

.sekai-audio-indicator.active,
.sekai-audio-indicator.is-active {
  background-color: rgb(var(--sekai-signal));
  box-shadow: 0 0 6px rgb(var(--sekai-signal));
}

/* ---------- Content ---------- */
.sekai-audio-content-simple {
  flex: 1;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  padding: 0 14px;
  position: relative;
  box-shadow: inset 4px 0 8px -4px rgb(0 0 0 / 0.1);
}

.sekai-audio-info-row {
  height: 14px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  margin-bottom: var(--sekai-space-xs);
}

.sekai-audio-visualizer {
  flex: 1;
  height: 100%;
  display: flex;
  align-items: flex-end;
  gap: 2px;
  margin-right: 10px;
  opacity: 0.3;
  transition: opacity var(--sekai-dur-slow);
}

.sekai-audio-player-simple.playing .sekai-audio-visualizer {
  opacity: 0.8;
}

.sekai-audio-visualizer-bar {
  flex: 1;
  min-width: 2px;
  max-width: 3px;
  margin-top: auto;
  background: rgb(var(--sekai-accent));
  border-radius: 1px 1px 0 0;
}

/* ---------- Self-driving bars ----------
   nightcord drives bar heights from JS every frame. Anything that
   doesn't (the React wrapper, a static preview) opts into a CSS
   animation instead by adding .sekai-viz--auto, so the visualizer is
   never a row of dead stubs. Per-bar delay comes from --i. */
.sekai-viz--auto .sekai-audio-visualizer-bar {
  height: 18%;
  animation: sekai-viz-height 900ms ease-in-out infinite alternate;
  animation-delay: calc(var(--i, 0) * -110ms);
  animation-play-state: paused;
}

/* The device's bars are full-height and scaled from the floor, so
   they animate transform rather than height. */
.sekai-viz--auto .sekai-viz-bar {
  animation: sekai-viz-scale 900ms ease-in-out infinite alternate;
  animation-delay: calc(var(--i, 0) * -110ms);
  animation-play-state: paused;
}

.playing .sekai-viz--auto .sekai-audio-visualizer-bar,
.playing.sekai-viz--auto .sekai-audio-visualizer-bar,
.playing .sekai-viz--auto .sekai-viz-bar,
.playing.sekai-viz--auto .sekai-viz-bar {
  animation-play-state: running;
}

@keyframes sekai-viz-height {
  from {
    height: 18%;
  }
  to {
    height: 95%;
  }
}

@keyframes sekai-viz-scale {
  from {
    transform: scaleY(0.12);
  }
  to {
    transform: scaleY(1);
  }
}

/* ---------- Oscilloscope ----------
   THE visualizer for this player. Not an alternative to the bars —
   the two players visualize different things on purpose:

     device  32-band spectrum   music: you want to see the mix
     simple  time-domain trace  audio: you want to see the utterance

   A voice note has no interesting spectrum; it has an envelope. So
   this one is a real <canvas> the component paints a waveform onto,
   at 200x20, DPI-aware.

   The canvas paints its own background (idle = --sekai-surface-card,
   playing = --sekai-surface-card-live at 25% alpha, which is what
   produces the afterglow trail), so CSS must not add an opacity of
   its own on top — it would double-dim the idle state.

   nightcord's renderer already emits this class
   (`sekai-renderer.js` `_createOscilloscope`) but nightcord's
   stylesheet never had a rule for it, so the canvas sat on the
   inline baseline with a descender gap under it. */
.sekai-audio-oscilloscope {
  display: block;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  margin-right: var(--sekai-space-sm);
  border-radius: 2px;
}

.sekai-audio-time {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-meta);
  font-weight: var(--sekai-weight-medium);
  color: rgb(255 255 255 / 0.5);
  text-align: right;
}

/* ---------- Progress ----------
   A 12px hit area around a 4px track: the target is generous, the
   mark is thin. */
.sekai-audio-progress-container {
  position: relative;
  z-index: 2;
  width: 100%;
  height: 12px;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.sekai-audio-track,
.sekai-audio-progress-bar {
  position: relative;
  width: 100%;
  height: 4px;
  background: var(--sekai-tint-sunken);
  border-radius: 2px;
  overflow: hidden;
  transition: height var(--sekai-dur) ease;
}

.sekai-audio-progress-container:hover .sekai-audio-track,
.sekai-audio-progress-container:hover .sekai-audio-progress-bar {
  height: 6px;
  background: rgb(0 0 0 / 0.3);
}

.sekai-audio-progress-fill {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 100%;
  background: rgb(var(--sekai-accent));
  border-radius: 0 2px 2px 0;
}

/* The scrub handle only exists while you are aiming at the bar. */
.sekai-audio-progress-fill::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 12px;
  height: 12px;
  background: #fff;
  border-radius: var(--sekai-radius-full);
  /* Same reasoning as the reply chip: a 12px dot needs the darker
     alpha to separate from the track behind it. */
  box-shadow: 0 2px 4px rgb(0 0 0 / 0.3);
  opacity: 0;
  transform: translate(50%, -50%);
  transition: opacity var(--sekai-dur);
}

/* Reveal on the 12px container, not only the 4px bar — the bar alone
   is a third of the real hit area, so the handle used to stay hidden
   while scrubbing was already possible. Bar selector kept as a
   superset. */
.sekai-audio-progress-bar:hover .sekai-audio-progress-fill::after,
.sekai-audio-progress-container:hover .sekai-audio-progress-fill::after {
  opacity: 1;
}

@media screen and (max-width: 600px) {
  .sekai-audio-player-simple {
    min-width: 100%;
    max-width: 100%;
  }
}



/* ==== layout/shell.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   App shell — sidebar + main
   ------------------------------------------------------------
   The nightcord chat frame, generalised into a two-pane shell.
   The sidebar is a fixed 280px rail that becomes an off-canvas
   drawer under 768px.
   ============================================================ */

.sekai-shell {
  display: flex;
  height: 100vh;
  height: 100dvh; /* phone browsers hide their toolbar mid-scroll */
  background: rgb(var(--sekai-canvas));
  overflow-x: hidden;
}

.sekai-sidebar {
  width: var(--sekai-sidebar-width);
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  background: rgb(var(--sekai-surface));
}

.sekai-sidebar__header {
  height: var(--sekai-bar-height);
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--sekai-space-lg);
  border-bottom: var(--sekai-hairline-divider);
  font-size: var(--sekai-text-md);
  font-weight: var(--sekai-weight-semibold);
  cursor: pointer;
  transition: background var(--sekai-dur);
}

.sekai-sidebar__header:hover {
  background: rgb(var(--sekai-surface-hover) / 0.5);
}

.sekai-sidebar__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--sekai-space-md) var(--sekai-space-sm) 0;
}

.sekai-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: rgb(var(--sekai-canvas));
  overflow-x: hidden;
}

/* ---------- Section label ----------
   Machine voice, but at reading size rather than signature size —
   it is a heading, not a stamp. */
.sekai-section-label {
  margin: var(--sekai-space-lg) 0 var(--sekai-space-2xs) var(--sekai-space-sm);
  font-size: var(--sekai-text-label);
  color: rgb(var(--sekai-fg-muted));
}

/* ---------- Channel ---------- */
.sekai-channel {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  margin-bottom: var(--sekai-space-sm);
  padding: var(--sekai-space-sm);
  border-radius: var(--sekai-radius-sm);
  background: rgb(var(--sekai-canvas));
  font-size: var(--sekai-text-lg);
  cursor: pointer;
  transition: background var(--sekai-dur);
}

.sekai-channel:hover {
  background: rgb(var(--sekai-surface-hover));
}

.sekai-channel.is-active {
  background: rgb(var(--sekai-surface-hover));
  color: rgb(var(--sekai-fg));
}

.sekai-channel__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
}

/* ---------- Roster ---------- */
.sekai-roster-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sekai-space-xs) var(--sekai-space-sm);
  border-radius: var(--sekai-radius-sm);
  cursor: pointer;
  transition: background var(--sekai-dur);
}

.sekai-roster-item:hover {
  background: rgb(var(--sekai-canvas));
}

.sekai-roster-item__info {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  min-width: 0;
}

.sekai-roster-item.is-offline {
  opacity: 0.6;
}

/* ---------- Off-canvas drawer ---------- */
@media screen and (max-width: 768px) {
  .sekai-sidebar {
    position: fixed;
    left: calc(-1 * var(--sekai-sidebar-width));
    top: 0;
    height: 100vh;
    height: 100dvh;
    z-index: var(--sekai-z-overlay);
    box-shadow: 2px 0 8px rgb(0 0 0 / 0.3);
    transition: left var(--sekai-dur-slow) ease;
  }

  .sekai-sidebar.is-open {
    left: 0;
  }

  .sekai-main {
    width: 100%;
  }
}

@media screen and (max-width: 480px) {
  .sekai-section-label {
    font-size: var(--sekai-text-meta);
  }
}

@media screen and (max-width: 768px) and (orientation: landscape) {
  .sekai-sidebar__header {
    height: 40px;
  }
}


/* ==== layout/chat.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Chat — bar, message list, message row, composer
   ============================================================ */

/* ---------- Top bar ---------- */
.sekai-bar {
  height: var(--sekai-bar-height);
  flex-shrink: 0;
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  padding: 0 var(--sekai-space-lg);
  background: rgb(var(--sekai-surface) / 0.8);
  border-bottom: var(--sekai-hairline-divider);
}

.sekai-bar__title {
  font-size: var(--sekai-text-md);
  font-weight: var(--sekai-weight-semibold);
}

/* Decorative traffic lights. Purely ornamental — they are not
   window controls and must never be the only way to do anything. */
.sekai-window-dots {
  margin-left: auto;
  display: flex;
  gap: var(--sekai-space-sm);
}

.sekai-window-dots > i {
  width: 12px;
  height: 12px;
  border-radius: var(--sekai-radius-full);
  display: inline-block;
}

.sekai-window-dots > i:nth-child(1) {
  background: #62c4ed;
}
.sekai-window-dots > i:nth-child(2) {
  background: #f5c03b;
}
.sekai-window-dots > i:nth-child(3) {
  background: #f25a64;
}

/* ---------- Message list ---------- */
.sekai-messages {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--sekai-space-lg) 0;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ---------- Message row ---------- */
.sekai-message {
  position: relative;
  display: flex;
  gap: var(--sekai-space-md);
  padding: var(--sekai-space-sm) var(--sekai-space-lg);
  transition: background var(--sekai-dur-instant);
}

/* Darken rather than lighten: on both worlds' canvases a dark wash
   reads as "under the cursor" without competing with the accent. */
.sekai-message:hover {
  background: rgb(4 4 5 / 0.07);
}

.sekai-message__body {
  flex: 1;
  min-width: 0;
}

.sekai-message__header {
  display: flex;
  align-items: baseline;
  gap: var(--sekai-space-sm);
  margin-bottom: var(--sekai-space-2xs);
}

.sekai-message__author {
  font-size: var(--sekai-text-lg);
  font-weight: var(--sekai-weight-semibold);
}

.sekai-message__time {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-message__text {
  margin: 0;
  font-size: var(--sekai-text-lg);
  word-break: break-word;
  overflow-wrap: break-word;
}

/* Being mentioned is the one row-level state that gets a colour.
   Warning amber, strip on the left, padding compensated so the text
   does not shift. */
.sekai-message.is-mentioned {
  background: rgb(var(--sekai-warning) / 0.1);
  border-left: 2px solid rgb(var(--sekai-warning));
  padding-left: 14px;
}

.sekai-message.is-mentioned:hover {
  background: rgb(var(--sekai-warning) / 0.15);
}

/* Jump-to-reply target: pulses once, then holds a tint long enough
   to find with your eyes. */
.sekai-message.is-highlighted {
  background: rgb(var(--sekai-accent-deep) / 0.2);
  animation: sekai-message-pulse 2s ease-out;
}

@keyframes sekai-message-pulse {
  0% {
    background: rgb(var(--sekai-accent-deep) / 0.4);
  }
  100% {
    background: rgb(var(--sekai-accent-deep) / 0.2);
  }
}

/* ---------- Hover action ----------
   Hidden by default, rises into place on row hover. pointer-events
   is off while hidden so it can never eat a click it isn't showing
   an affordance for. */
.sekai-message__action {
  position: absolute;
  top: -12px;
  right: var(--sekai-space-md);
  z-index: var(--sekai-z-raised);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: rgb(var(--sekai-surface));
  border: var(--sekai-hairline-divider);
  border-radius: var(--sekai-radius-xs);
  /* Off-ladder on purpose, one alpha step darker than elev-1. At 32px
     this chip has almost no area to cast with, so it needs a tighter,
     darker shadow than a card does to read as detached at all. */
  box-shadow: 0 2px 4px rgb(0 0 0 / 0.3);
  color: rgb(var(--sekai-fg-muted));
  cursor: pointer;
  opacity: 0;
  transform: translateY(4px) scale(0.95);
  pointer-events: none;
  transition:
    opacity var(--sekai-dur-fast) ease-out,
    transform var(--sekai-dur-fast) ease-out,
    background var(--sekai-dur-instant);
}

.sekai-message:hover .sekai-message__action {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.sekai-message__action:hover {
  background: rgb(var(--sekai-canvas));
  border-color: rgb(var(--sekai-line-strong) / var(--sekai-line-strong-a));
  color: rgb(var(--sekai-fg));
  /* Likewise: 8px spread, not elev-2's 12px. The chip lifts, it does
     not bloom. */
  box-shadow: 0 4px 8px rgb(0 0 0 / 0.4);
  transform: translateY(0) scale(1.05);
}

.sekai-message__action:active {
  transform: translateY(0) scale(0.95);
}

.sekai-message__action svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}

/* ---------- Composer ---------- */
.sekai-composer {
  position: relative;
  padding: var(--sekai-space-lg);
  overflow: visible; /* upload menus and progress escape upward */
}

.sekai-composer__actions {
  display: flex;
  align-self: flex-start;
  gap: var(--sekai-space-sm);
  margin-top: 5px;
}

/* ---------- Suggestion list ----------
   Mentions and sticker autocomplete. Anchored above the composer. */
.sekai-suggest {
  position: absolute;
  bottom: 100%;
  left: 0;
  z-index: var(--sekai-z-modal);
  width: 240px;
  max-height: 240px;
  margin-bottom: var(--sekai-space-sm);
  background: rgb(var(--sekai-surface-raised));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-xs);
  box-shadow: 0 -4px 12px rgb(0 0 0 / 0.3);
  overflow-y: auto;
}

.sekai-suggest[hidden],
.sekai-suggest.is-hidden {
  display: none;
}

.sekai-suggest__item {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-sm);
  padding: var(--sekai-space-sm) var(--sekai-space-md);
  color: rgb(var(--sekai-fg-muted));
  font-size: var(--sekai-text-md);
  cursor: pointer;
}

.sekai-suggest__item:hover,
.sekai-suggest__item.is-active {
  background: rgb(var(--sekai-surface-hover));
  color: rgb(var(--sekai-fg));
}

.sekai-suggest__preview {
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  object-fit: contain;
  margin-right: var(--sekai-space-2xs);
  background: rgb(0 0 0 / 0.1);
  border-radius: var(--sekai-radius-xs);
}

.sekai-suggest__label {
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-fg));
  font-size: var(--sekai-text-md);
}

.sekai-suggest__desc {
  font-size: var(--sekai-text-label);
  color: rgb(var(--sekai-fg-muted));
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Responsive ---------- */
@media screen and (max-width: 768px) {
  .sekai-messages {
    padding: var(--sekai-space-md) 0;
  }

  .sekai-message {
    padding: var(--sekai-space-sm) var(--sekai-space-md);
  }

  .sekai-window-dots {
    display: none;
  }

  .sekai-composer {
    padding: var(--sekai-space-md);
  }
}

@media screen and (max-width: 480px) {
  .sekai-message__author,
  .sekai-message__text {
    font-size: var(--sekai-text-md);
  }

  .sekai-message__time {
    font-size: var(--sekai-text-meta);
  }

  /* Opaque and safe-area padded: the composer must cover whatever
     scrolls under it and clear the iPhone home indicator. */
  .sekai-composer {
    padding: var(--sekai-space-sm);
    padding-bottom: max(var(--sekai-space-sm), env(safe-area-inset-bottom));
    background: rgb(var(--sekai-canvas));
  }

  .sekai-composer .sekai-input-shell {
    min-height: 44px;
    padding: var(--sekai-space-xs) 10px;
    gap: var(--sekai-space-sm);
  }

  .sekai-composer__actions {
    gap: 2px;
  }

  .sekai-suggest {
    width: calc(100vw - 48px);
    max-width: 280px;
  }
}

@media screen and (max-width: 768px) and (orientation: landscape) {
  .sekai-bar {
    height: 40px;
  }

  .sekai-composer {
    padding: var(--sekai-space-sm) var(--sekai-space-md);
  }
}


/* ==== layout/auth.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Auth — the SEKAI Pass screens
   ------------------------------------------------------------
   One centred panel on a grid field, capped with an accent
   hairline. Everything on these screens is either the thing you
   are deciding about (large, sans) or the machinery behind it
   (small, mono, dim).
   ============================================================ */

/* Put this on <body> for a single-panel auth page. */
.sekai-auth-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: var(--sekai-space-lg);
  background-color: rgb(var(--sekai-canvas));
  background-image:
    linear-gradient(rgb(255 255 255 / 0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgb(255 255 255 / 0.03) 1px, transparent 1px);
  background-size: 40px 40px;
}

.sekai-auth {
  position: relative;
  width: min(90vw, 350px);
  /* 42px is the Pass panel's own padding — deliberately off the 8px
     ladder. It is what makes the card feel unhurried; the nearest
     ladder step (32px) reads cramped and 48px reads slack. */
  padding: 42px;
  background: rgb(var(--sekai-surface));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-md);
  box-shadow: var(--sekai-elev-4);
  overflow: hidden;
}

/* The cap. One per screen. */
.sekai-auth::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: linear-gradient(
    90deg,
    rgb(var(--sekai-accent-deep)),
    rgb(var(--sekai-accent)),
    rgb(var(--sekai-accent-deep))
  );
}

.sekai-auth--wide {
  width: min(90vw, 500px);
}

/* ---------- Logo + system signature ---------- */
.sekai-auth__logo {
  position: relative;
  margin-bottom: var(--sekai-space-3xl);
  text-align: center;
}

.sekai-auth__logo img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
  border-radius: var(--sekai-radius-xs);
}

/* Set data-sig to change the stamp; it defaults to the Pass one. */
.sekai-auth__logo::after {
  content: "SEKAI PASS // SYSTEM";
  display: block;
  margin-top: var(--sekai-space-md);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-micro);
  letter-spacing: var(--sekai-tracking-widest);
  color: rgb(var(--sekai-fg-muted));
  opacity: 0.6;
}

.sekai-auth__logo[data-sig]::after {
  content: attr(data-sig);
}

/* ---------- Header ---------- */
.sekai-auth__header {
  margin-bottom: 30px;
  padding-bottom: var(--sekai-space-2xl);
  border-bottom: var(--sekai-hairline-divider);
  text-align: center;
}

.sekai-auth__title {
  margin-bottom: var(--sekai-space-xs);
  font-size: var(--sekai-text-xl);
  font-weight: var(--sekai-weight-semibold);
  letter-spacing: var(--sekai-tracking-tight);
  color: rgb(var(--sekai-fg));
}

.sekai-auth__subtitle {
  margin-bottom: var(--sekai-space-3xl);
  font-size: var(--sekai-text-sm);
  line-height: var(--sekai-leading);
  color: rgb(var(--sekai-fg-muted));
  text-align: center;
}

/* ---------- Connection visual ----------
   Two entities and the live line between them. This is the whole
   argument of an OAuth consent screen in one picture: who is
   talking to whom, and that it is happening now. */
.sekai-connection {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 10px 0 30px;
  padding: 0 10px;
}

.sekai-entity {
  position: relative;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.sekai-entity__avatar {
  margin-bottom: 10px;
}

.sekai-entity__label {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-micro);
  font-weight: var(--sekai-weight-semibold);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: rgb(var(--sekai-fg-muted));
}

/* Negative bottom margin lifts the line onto the avatars' centre
   line rather than the label's. */
.sekai-connection .sekai-flow {
  margin: 0 var(--sekai-space-lg) -22px;
}

.sekai-connection__badge {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  transform: translate(-50%, -50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  background: rgb(var(--sekai-surface));
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-full);
  box-shadow: 0 0 10px rgb(var(--sekai-surface));
  color: rgb(var(--sekai-fg-muted));
  font-size: var(--sekai-text-label);
}

/* ---------- Scope rows ---------- */
.sekai-scope__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  background: rgb(60 60 70 / 0.3);
  border: 1px solid rgb(255 255 255 / 0.05);
  border-radius: var(--sekai-radius-sm);
  color: rgb(var(--sekai-accent));
}

.sekai-scope__content {
  flex: 1;
  min-width: 0;
  padding-top: 1px;
}

.sekai-scope__name {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sekai-space-sm);
  margin-bottom: var(--sekai-space-2xs);
  font-size: var(--sekai-text-sm);
  font-weight: var(--sekai-weight-semibold);
  color: rgb(var(--sekai-fg));
}

.sekai-scope__desc {
  font-size: var(--sekai-text-label);
  line-height: var(--sekai-leading-tight);
  color: rgb(var(--sekai-fg-muted));
}

/* ---------- Footnotes ---------- */
.sekai-security-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sekai-space-sm);
  margin-top: var(--sekai-space-xl);
  padding-top: var(--sekai-space-lg);
  border-top: var(--sekai-hairline-divider);
  font-size: var(--sekai-text-meta);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-privacy-note {
  margin-top: var(--sekai-space-2xl);
  text-align: center;
  font-size: var(--sekai-text-meta);
  line-height: var(--sekai-leading-relaxed);
  color: rgb(255 255 255 / 0.2);
}

.sekai-auth__link {
  margin-top: var(--sekai-space-3xl);
  text-align: center;
  font-size: var(--sekai-text-sm);
  color: rgb(var(--sekai-fg-muted));
}

.sekai-auth__link a {
  color: rgb(var(--sekai-fg-muted));
  text-decoration: underline;
  text-underline-offset: 4px;
  cursor: pointer;
  transition: color var(--sekai-dur);
}

.sekai-auth__link a:hover {
  color: rgb(var(--sekai-accent));
}

/* ---------- Captcha slot ----------
   Reserves the widget's height so the panel doesn't jump when a
   third-party iframe finally paints. */
.sekai-captcha-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 65px;
  margin: var(--sekai-space-lg) 0;
}

/* ---------- Responsive ---------- */
@media (max-width: 768px) {
  .sekai-auth {
    padding: var(--sekai-space-2xl);
  }

  .sekai-connection {
    flex-direction: column;
    gap: var(--sekai-space-xl);
  }

  .sekai-connection .sekai-flow {
    flex: none;
    width: 1px;
    height: 40px;
    margin: 0;
  }

  /* The horizontal packet animation has to become a vertical one —
     see .sekai-flow--vertical in signatures.css. */
  .sekai-connection .sekai-flow::before {
    width: 3px;
    height: 100%;
    left: 50%;
    top: 0;
    transform: translateX(-50%);
    background: linear-gradient(
      180deg,
      transparent 0%,
      transparent 30%,
      rgb(var(--sekai-accent)) 50%,
      transparent 70%,
      transparent 100%
    );
    background-size: 100% 200%;
    animation: sekai-flow-y 2s infinite linear;
  }
}

@media (max-width: 480px) {
  .sekai-auth__logo img {
    width: 200px;
  }
}


/* ==== layout/page.css ==== */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-FileCopyrightText: 2026 The 25-ji-code-de Team */

/* ============================================================
   Page furniture — headers, settings, footer
   ============================================================ */

.sekai-page {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: var(--sekai-space-2xl) var(--sekai-space-lg);
}

.sekai-page-header {
  margin-bottom: var(--sekai-space-2xl);
  text-align: center;
}

.sekai-page-header h1,
.sekai-page-header h2 {
  margin: 0 0 var(--sekai-space-2xs);
  color: rgb(var(--sekai-fg));
}

.sekai-page-subtitle {
  margin: 0;
  font-size: var(--sekai-text-md);
  color: rgb(var(--sekai-fg-muted));
}

/* ---------- Settings header ----------
   The section title itself speaks machine: it names a subsystem,
   not a topic. */
.sekai-settings-header {
  margin-bottom: var(--sekai-space-3xl);
  padding-bottom: var(--sekai-space-lg);
  border-bottom: var(--sekai-hairline-divider);
}

.sekai-settings-header h2 {
  margin: 0;
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-md);
  letter-spacing: var(--sekai-tracking-widest);
  text-transform: uppercase;
  color: rgb(var(--sekai-accent));
}

/* ---------- Profile block ----------
   Dashed border: this block is a readout, not an editable form. The
   dash is the tell. */
.sekai-profile {
  display: flex;
  align-items: center;
  gap: var(--sekai-space-2xl);
  margin-bottom: var(--sekai-space-3xl);
  padding: var(--sekai-space-xl);
  background: var(--sekai-tint-raised);
  border: 1px dashed rgb(var(--sekai-line) / var(--sekai-line-a));
  border-radius: var(--sekai-radius-control);
}

.sekai-profile__meta {
  flex: 1;
  min-width: 0;
}

.sekai-meta-row {
  display: flex;
  margin-bottom: var(--sekai-space-sm);
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
}

.sekai-meta-row:last-child {
  margin-bottom: 0;
}

.sekai-meta-row__key {
  width: 80px;
  flex-shrink: 0;
  color: rgb(var(--sekai-fg-muted));
}

.sekai-meta-row__value {
  color: rgb(var(--sekai-fg));
  min-width: 0;
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---------- Key/value readout ----------
   Bordered rows, label left in mono, value right in sans. */
.sekai-readout {
  margin-bottom: 30px;
  border: var(--sekai-hairline);
  border-radius: var(--sekai-radius-control);
  background: rgb(0 0 0 / 0.125);
  overflow: hidden;
}

.sekai-readout p {
  display: flex;
  justify-content: space-between;
  gap: var(--sekai-space-lg);
  margin: 0;
  padding: var(--sekai-space-lg);
  border-bottom: var(--sekai-hairline-divider);
  font-size: var(--sekai-text-md);
}

.sekai-readout p:last-child {
  border-bottom: none;
}

.sekai-readout strong {
  font-family: var(--sekai-font-mono);
  font-size: var(--sekai-text-label);
  font-weight: var(--sekai-weight-normal);
  text-transform: uppercase;
  letter-spacing: var(--sekai-tracking-wide);
  color: rgb(var(--sekai-fg-muted));
}

/* ---------- Lists ---------- */
.sekai-uri-list {
  margin: 0;
  padding-left: 1.1rem;
}

.sekai-uri-list li {
  margin-bottom: 2px;
  overflow-wrap: anywhere;
}

/* ---------- Footer ---------- */
.sekai-footer {
  margin-top: var(--sekai-space-2xl);
  padding-bottom: var(--sekai-space-2xl);
  text-align: center;
  font-size: var(--sekai-text-label);
  color: rgb(var(--sekai-fg-muted));
  opacity: 0.8;
}

.sekai-footer a {
  margin: 0 var(--sekai-space-sm);
  color: inherit;
  transition: color var(--sekai-dur);
}

.sekai-footer a:hover {
  color: rgb(var(--sekai-accent));
}

/* ---------- Responsive ---------- */
@media (max-width: 480px) {
  .sekai-profile {
    flex-direction: column;
    text-align: center;
  }

  .sekai-meta-row {
    justify-content: center;
  }
}


