From daad4ccac99b762c4e37472840ddc86f17f20752 Mon Sep 17 00:00:00 2001 From: Armor00 <2654988228@qq.com> Date: Mon, 1 Jun 2026 15:28:50 +0800 Subject: [PATCH] feat: add DockingEvent model and docking styles - Add DockingEvent model with parent/child asset relations, docked_at/undocked_at timestamps - Add docking migration for new docking_events table - Add .ad-dock, .ad-dock-box, .ad-dock-item, .ad-undock CSS classes Co-Authored-By: Claude Opus 4.7 --- app/models.py | 44 +- app/static/styles.css | 3164 ++--------------- .../2a4c9d8e7f10_add_docking_events.py | 46 + 3 files changed, 363 insertions(+), 2891 deletions(-) create mode 100644 migrations/versions/2a4c9d8e7f10_add_docking_events.py diff --git a/app/models.py b/app/models.py index 69a8961..f1d5721 100644 --- a/app/models.py +++ b/app/models.py @@ -145,6 +145,44 @@ class Asset(TimestampMixin, db.Model): lazy="selectin", order_by="AssetLogEntry.start_at", ) + docking_children: Mapped[list["DockingEvent"]] = relationship( + back_populates="parent_asset", + cascade="all, delete-orphan", + foreign_keys="DockingEvent.parent_asset_id", + lazy="selectin", + ) + docking_parents: Mapped[list["DockingEvent"]] = relationship( + back_populates="child_asset", + cascade="all, delete-orphan", + foreign_keys="DockingEvent.child_asset_id", + lazy="selectin", + ) + + +class DockingEvent(TimestampMixin, db.Model): + __tablename__ = "docking_events" + + id: Mapped[uuid.UUID] = mapped_column(Uuid, primary_key=True, default=uuid.uuid4) + parent_asset_id: Mapped[uuid.UUID] = mapped_column( + Uuid, ForeignKey("assets.id", ondelete="CASCADE"), nullable=False, index=True + ) + child_asset_id: Mapped[uuid.UUID | None] = mapped_column( + Uuid, ForeignKey("assets.id", ondelete="SET NULL"), nullable=True, index=True + ) + child_label: Mapped[str | None] = mapped_column(String(255)) + docked_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True) + undocked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True) + dock_note: Mapped[str | None] = mapped_column(Text) + undock_note: Mapped[str | None] = mapped_column(Text) + + parent_asset: Mapped[Asset] = relationship( + back_populates="docking_children", + foreign_keys=[parent_asset_id], + ) + child_asset: Mapped[Asset | None] = relationship( + back_populates="docking_parents", + foreign_keys=[child_asset_id], + ) class AssetLogEntry(TimestampMixin, db.Model): @@ -156,9 +194,7 @@ class AssetLogEntry(TimestampMixin, db.Model): ) entry_kind: Mapped[str] = mapped_column(String(16), nullable=False) title: Mapped[str] = mapped_column(String(255), nullable=False) - state_label: Mapped[str | None] = mapped_column(String(120)) mission_label: Mapped[str | None] = mapped_column(String(255)) - location: Mapped[str | None] = mapped_column(String(255)) start_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True) end_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) summary: Mapped[str | None] = mapped_column(Text) @@ -183,5 +219,9 @@ class AssetStateNode(TimestampMixin, db.Model): title: Mapped[str] = mapped_column(String(255), nullable=False) detail: Mapped[str | None] = mapped_column(Text) at_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True) + target_location: Mapped[str | None] = mapped_column(String(255)) + previous_location: Mapped[str | None] = mapped_column(String(255)) + transit_location: Mapped[str | None] = mapped_column(String(255)) + state_label: Mapped[str | None] = mapped_column(String(120)) entry: Mapped[AssetLogEntry] = relationship(back_populates="state_nodes") diff --git a/app/static/styles.css b/app/static/styles.css index 100cbd8..6ac9514 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -1,2890 +1,276 @@ +/* ═══ KSP管理后台 Design System ═══ */ +/* ═══ Phase 1: CSS Variables + Layout + Components ═══ */ + +/* ─── Dark Theme (Default) ─── */ :root { - color-scheme: light; - --bg: #e7f0fb; - --bg-accent: #bfd8ff; - --bg-top: #f3f8ff; - --bg-bottom: #e4eefb; - --bg-radial-1: rgba(23, 103, 213, 0.18); - --bg-radial-2: rgba(73, 156, 255, 0.16); - --surface: rgba(245, 250, 255, 0.84); - --surface-strong: rgba(255, 255, 255, 0.9); - --surface-soft: rgba(255, 255, 255, 0.72); - --surface-soft-strong: rgba(255, 255, 255, 0.78); - --surface-highlight: rgba(248, 251, 255, 0.96); - --surface-raised: rgba(255, 255, 255, 0.82); - --surface-hero-start: rgba(250, 253, 255, 0.95); - --surface-hero-end: rgba(224, 237, 255, 0.84); - --surface-panel-start: rgba(248, 251, 255, 0.88); - --surface-panel-end: rgba(241, 247, 255, 0.88); - --text: #12233e; - --muted: #5b6f8f; - --line: rgba(18, 35, 62, 0.12); - --primary: #1767d5; - --primary-strong: #0d4fa8; - --danger: #b63439; - --warning: #1b7cf2; - --shadow: 0 18px 48px rgba(18, 48, 96, 0.09); - --primary-soft: rgba(23, 103, 213, 0.08); - --primary-soft-strong: rgba(23, 103, 213, 0.14); - --primary-line: rgba(23, 103, 213, 0.16); - --primary-line-strong: rgba(23, 103, 213, 0.28); - --neutral-soft: rgba(91, 111, 143, 0.08); - --neutral-soft-strong: rgba(91, 111, 143, 0.16); - --danger-soft: rgba(182, 52, 57, 0.08); - --danger-line: rgba(182, 52, 57, 0.25); - --success-line: rgba(15, 107, 91, 0.28); - --warning-line: rgba(201, 105, 39, 0.28); - --timeline-grid: rgba(18, 35, 62, 0.08); - --timeline-grid-soft: rgba(18, 35, 62, 0.06); - --timeline-label-shadow: 14px 0 18px -18px rgba(18, 35, 62, 0.28); - --timeline-label-shadow-active: 14px 0 20px -18px rgba(18, 35, 62, 0.32), 0 0 0 3px rgba(23, 103, 213, 0.08); - --theme-toggle-bg: rgba(255, 255, 255, 0.78); - --theme-toggle-border: rgba(18, 35, 62, 0.12); - --theme-toggle-track: rgba(23, 103, 213, 0.16); - --theme-toggle-thumb: linear-gradient(135deg, #1767d5, #0d4fa8); - --tooltip-bg: rgba(18, 35, 62, 0.96); - --tooltip-text: #fff; - --radius-lg: 28px; - --radius-md: 18px; - --font-main: "Noto Sans SC", "Microsoft YaHei UI", "Segoe UI Variable", sans-serif; -} - -html[data-theme="dark"] { - color-scheme: dark; - --bg: #0d1522; - --bg-accent: #1d3153; - --bg-top: #111b2b; - --bg-bottom: #09111d; - --bg-radial-1: rgba(91, 149, 255, 0.2); - --bg-radial-2: rgba(49, 99, 186, 0.24); - --surface: rgba(17, 25, 39, 0.84); - --surface-strong: rgba(18, 28, 44, 0.92); - --surface-soft: rgba(18, 28, 44, 0.76); - --surface-soft-strong: rgba(21, 32, 49, 0.82); - --surface-highlight: rgba(24, 36, 54, 0.96); - --surface-raised: rgba(23, 34, 51, 0.88); - --surface-hero-start: rgba(22, 34, 52, 0.94); - --surface-hero-end: rgba(12, 19, 31, 0.92); - --surface-panel-start: rgba(24, 36, 54, 0.9); - --surface-panel-end: rgba(16, 25, 39, 0.92); - --text: #e8effc; - --muted: #9eafcb; - --line: rgba(160, 183, 219, 0.18); - --primary: #7eb5ff; - --primary-strong: #b2d0ff; - --danger: #ff9a96; - --warning: #8ac2ff; - --shadow: 0 22px 52px rgba(0, 0, 0, 0.34); - --primary-soft: rgba(96, 149, 255, 0.14); - --primary-soft-strong: rgba(96, 149, 255, 0.24); - --primary-line: rgba(132, 176, 255, 0.24); - --primary-line-strong: rgba(132, 176, 255, 0.34); - --neutral-soft: rgba(121, 140, 173, 0.14); - --neutral-soft-strong: rgba(121, 140, 173, 0.22); - --danger-soft: rgba(255, 124, 124, 0.1); - --danger-line: rgba(255, 154, 150, 0.24); - --success-line: rgba(71, 198, 176, 0.28); - --warning-line: rgba(255, 179, 102, 0.24); - --timeline-grid: rgba(165, 184, 214, 0.1); - --timeline-grid-soft: rgba(165, 184, 214, 0.08); - --timeline-label-shadow: 16px 0 22px -20px rgba(0, 0, 0, 0.5); - --timeline-label-shadow-active: 16px 0 22px -20px rgba(0, 0, 0, 0.54), 0 0 0 3px rgba(96, 149, 255, 0.14); - --theme-toggle-bg: rgba(18, 28, 44, 0.84); - --theme-toggle-border: rgba(160, 183, 219, 0.2); - --theme-toggle-track: rgba(96, 149, 255, 0.24); - --theme-toggle-thumb: linear-gradient(135deg, #9bc5ff, #6b9bff); - --tooltip-bg: rgba(7, 12, 21, 0.98); - --tooltip-text: #eef4ff; -} - -* { - box-sizing: border-box; -} - -html { - background: var(--bg-bottom); -} - -body { - margin: 0; - min-height: 100vh; - color: var(--text); - font-family: var(--font-main); - background: - radial-gradient(circle at top left, var(--bg-radial-1), transparent 24%), - radial-gradient(circle at top right, var(--bg-radial-2), transparent 26%), - linear-gradient(180deg, var(--bg-top) 0%, var(--bg-bottom) 100%); - transition: background 180ms ease, color 180ms ease; -} - -a, -button, -input, -select, -textarea { - transition: - background-color 180ms ease, - border-color 180ms ease, - color 180ms ease, - box-shadow 180ms ease, - transform 180ms ease; -} - -.page-shell { - width: calc(100vw - 16px); - max-width: none; - margin: 0 auto; - padding: 14px 0 36px; -} - -.site-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - margin-bottom: 28px; -} - -.site-header-tools { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 14px; - flex-wrap: wrap; -} - -.brand { - display: inline-flex; - flex-direction: column; - text-decoration: none; - color: var(--text); -} - -.brand-kicker { - font-size: 12px; - letter-spacing: 0.32em; - text-transform: uppercase; - color: var(--muted); -} - -.brand-name { - font-size: 28px; - font-weight: 700; -} - -.site-nav { - display: flex; - align-items: center; - gap: 16px; - flex-wrap: wrap; -} - -.site-nav a { - color: var(--text); - text-decoration: none; - padding: 9px 12px; - border-radius: 999px; - background: color-mix(in srgb, var(--surface-strong) 72%, transparent); - border: 1px solid var(--line); -} - -.theme-toggle { - display: inline-flex; - align-items: center; - gap: 10px; - min-height: 44px; - padding: 7px 12px; - border-radius: 999px; - border: 1px solid var(--theme-toggle-border); - background: var(--theme-toggle-bg); - color: var(--text); - box-shadow: var(--shadow); - cursor: pointer; -} - -.theme-toggle:hover, -.theme-toggle:focus-visible, -.site-nav a:hover, -.site-nav a:focus-visible { - border-color: var(--primary-line-strong); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); -} - -.theme-toggle:focus-visible, -.site-nav a:focus-visible { - outline: none; -} - -.theme-toggle-track { - position: relative; - width: 42px; - height: 24px; - flex: 0 0 42px; - border-radius: 999px; - background: var(--theme-toggle-track); -} - -.theme-toggle-thumb { - position: absolute; - top: 3px; - left: 3px; - width: 18px; - height: 18px; - border-radius: 999px; - background: var(--theme-toggle-thumb); - box-shadow: 0 6px 14px rgba(18, 35, 62, 0.24); - transition: transform 180ms ease, background 180ms ease, box-shadow 180ms ease; -} - -html[data-theme="dark"] .theme-toggle-thumb { - transform: translateX(18px); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.36); -} - -.theme-toggle-copy { - display: grid; - gap: 1px; - text-align: left; -} - -.theme-toggle-label { - font-size: 11px; - font-weight: 700; - letter-spacing: 0.12em; - text-transform: uppercase; - color: var(--muted); -} - -.theme-toggle-value { - font-size: 13px; - font-weight: 700; -} - -.flash-stack { - display: grid; - gap: 12px; - margin-bottom: 20px; -} - -.flash-message { - padding: 14px 16px; - border-radius: 16px; - border: 1px solid var(--line); - background: color-mix(in srgb, var(--surface-strong) 78%, transparent); -} - -.flash-message.success { - border-color: var(--success-line); -} - -.flash-message.error { - border-color: var(--warning-line); -} - -.form-inline-alert { - margin-bottom: 16px; -} - -.field-error-shell { - position: relative; -} - -.field-error-input, -.field-error-shell input:not([type="hidden"]), -.field-error-shell textarea, -.field-error-shell select { - border-color: var(--warning-line) !important; - box-shadow: 0 0 0 3px rgba(214, 138, 38, 0.12); - background: rgba(255, 248, 236, 0.92); -} - -.validation-dialog { - width: min(560px, calc(100vw - 32px)); - padding: 0; - border: 0; - background: transparent; -} - -.validation-dialog::backdrop { - background: rgba(12, 20, 35, 0.38); - backdrop-filter: blur(6px); -} - -.validation-dialog-card { - margin: 0; - padding: 20px 22px; - border-radius: 22px; - border: 1px solid var(--line); - background: color-mix(in srgb, var(--surface-strong) 92%, white 8%); - box-shadow: var(--shadow); -} - -.validation-dialog-card h2 { - margin: 0; -} - -.validation-dialog-list { - margin: 14px 0 0; - padding-left: 20px; - display: grid; - gap: 8px; -} - -.hero-panel, -.panel, -.metric-card { - border: 1px solid var(--line); - box-shadow: var(--shadow); - backdrop-filter: blur(12px); -} - -.hero-panel { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 24px; - padding: 28px; - border-radius: var(--radius-lg); - background: linear-gradient(135deg, var(--surface-hero-start), var(--surface-hero-end)); -} - -.hero-panel.compact { - grid-template-columns: 1fr; -} - -.page-header-compact { - width: min(980px, 100%); - padding: 12px 14px; - gap: 10px; -} - -.page-header-compact .eyebrow { - margin-bottom: 4px; - font-size: 10px; -} - -.page-header-compact .hero-copy h1 { - font-size: clamp(22px, 2vw, 30px); - max-width: none; - line-height: 1; -} - -.page-header-compact .hero-text { - margin: 4px 0 0; - font-size: 13px; - line-height: 1.35; - max-width: 72ch; -} - -.engine-detail-hero { - width: min(720px, 100%); - padding: 8px 12px; - gap: 8px; -} - -.engine-detail-hero .hero-copy h1 { - font-size: clamp(20px, 1.9vw, 28px); - max-width: none; - line-height: 1; -} - -.engine-detail-hero .eyebrow { - margin-bottom: 3px; - font-size: 10px; -} - -.engine-detail-hero .hero-text { - margin: 4px 0 0; - font-size: 11px; - line-height: 1.2; - max-width: 58ch; -} - -.eyebrow { - margin: 0 0 10px; - font-size: 12px; - font-weight: 700; - letter-spacing: 0.22em; - text-transform: uppercase; - color: var(--primary); -} - -.hero-copy h1, -.panel-heading h2 { - margin: 0; - line-height: 1.1; -} - -.hero-copy h1 { - font-size: clamp(34px, 4vw, 56px); - max-width: 12ch; -} - -.hero-text, -.panel p, -.module-card p { - color: var(--muted); - line-height: 1.7; -} - -.hero-callout { - padding: 22px; - border-radius: var(--radius-md); - background: var(--primary-soft); - border: 1px solid var(--primary-line); -} - -.callout-label, -.callout-subtext { - margin: 0; - color: var(--muted); -} - -.callout-number { - margin: 12px 0 10px; - font-size: 72px; - font-weight: 700; - line-height: 1; - color: var(--warning); -} - -.callout-text { - margin: 0 0 10px; - font-weight: 600; -} - -.metric-grid, -.content-grid, -.decision-grid, -.factor-grid, -.engine-grid, -.detail-grid, -.summary-grid { - display: grid; - gap: 20px; -} - -.metric-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)); - margin: 22px 0; -} - -.metric-card { - display: flex; - flex-direction: column; - gap: 8px; - padding: 20px; - border-radius: var(--radius-md); - background: var(--surface); -} - -.metric-value { - font-size: 34px; - font-weight: 700; -} - -.metric-label { - color: var(--muted); -} - -.content-grid { - grid-template-columns: 1.4fr 1fr; - margin-bottom: 22px; -} - -.fuel-layout { - align-items: start; -} - -.panel { - padding: 18px; - border-radius: var(--radius-lg); - background: var(--surface); -} - -.catalog-header { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 20px; - align-items: start; -} - -.catalog-header-tight { - padding: 10px 14px; - gap: 12px; -} - -.catalog-page-header { - grid-template-columns: minmax(0, 1.15fr) minmax(320px, 0.95fr); - justify-content: stretch; - align-items: stretch; -} - -.catalog-header-copy { - min-width: 0; - display: grid; - align-content: center; -} - -.catalog-header-copy .eyebrow { - margin-bottom: 4px; -} - -.catalog-header-copy h1 { - margin: 0; - font-size: clamp(24px, 2.2vw, 32px); - line-height: 1; -} - -.catalog-header-copy .hero-text { - margin: 6px 0 0; - font-size: 14px; - line-height: 1.35; - max-width: 52ch; -} - -.engine-catalog-hero { - align-items: stretch; -} - -.catalog-header-side { - display: grid; - gap: 10px; - align-content: start; - justify-self: stretch; -} - -.catalog-metrics { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; - align-self: start; - justify-self: stretch; - min-width: 0; - width: 100%; -} - -.catalog-metrics-duo { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.catalog-header-time-form { - display: grid; - grid-template-columns: minmax(240px, 1fr) auto; - gap: 10px; - align-items: end; -} - -.catalog-header-time-form .mission-datetime-control { - margin: 0; -} - -.catalog-header-time-form .secondary-button { - white-space: nowrap; -} - -.catalog-header-tight .summary-card { - padding: 10px 12px; - border-radius: 14px; - min-height: 74px; - display: grid; - align-content: space-between; -} - -.catalog-header-tight .summary-label { - margin-bottom: 4px; - font-size: 10px; -} - -.catalog-header-tight .summary-card.compact strong { - font-size: clamp(18px, 1.6vw, 28px); - line-height: 1.05; -} - -.catalog-sidebar { - display: grid; - gap: 12px; - align-content: start; -} - -.catalog-actions-panel { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 10px 12px; -} - -.catalog-actions-panel .eyebrow { - margin-bottom: 2px; - font-size: 10px; -} - -.catalog-actions-panel h2 { - margin: 0; - font-size: 17px; - line-height: 1.1; -} - -.catalog-actions-panel .small-muted { - margin: 2px 0 0; -} - -.summary-card.compact strong { - font-size: 24px; -} - -.panel-wide { - min-width: 0; -} - -.top-gap { - margin-top: 18px; -} - -.panel-heading { - margin-bottom: 18px; -} - -.decision-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.decision-grid h3, -.module-card h3, -.factor-card h3 { - margin: 0 0 8px; -} - -.module-list { - display: grid; - gap: 14px; -} - -.module-card { - padding: 16px; - border-radius: 16px; - background: var(--surface-strong); - border: 1px solid var(--line); -} - -.module-card.live { - border-color: rgba(23, 103, 213, 0.28); -} - -.module-meta { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.module-status { - font-size: 12px; - font-weight: 700; - letter-spacing: 0.16em; - text-transform: uppercase; - color: var(--primary); -} - -.module-link { - display: inline-flex; - margin-top: 12px; - color: var(--primary-strong); - font-weight: 700; - text-decoration: none; -} - -.module-link.muted { - color: var(--muted); -} - -.secondary-button { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 9px 12px; - border-radius: 999px; - border: 1px solid var(--line); - background: var(--surface-soft); - color: var(--text); - text-decoration: none; - font-weight: 600; -} - -.filter-actions { - display: flex; - gap: 12px; - flex-wrap: wrap; -} - -.catalog-layout { - display: grid; - grid-template-columns: clamp(220px, 15vw, 248px) minmax(0, 1fr); - gap: 14px; - margin-top: 14px; - align-items: start; -} - -.filter-panel { - position: sticky; - top: 8px; -} - -.filter-panel-tight { - padding: 14px; -} - -.filter-form { - display: grid; - gap: 10px; -} - -.filter-form input, -.filter-form select { - width: 100%; - padding: 9px 10px; - border-radius: 10px; - border: 1px solid var(--line); - background: var(--surface-soft-strong); - color: var(--text); - font: inherit; -} - -.catalog-panel { - padding: 12px 14px; -} - -.results-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 16px; - margin-bottom: 12px; -} - -.button-row { - display: flex; - align-items: center; - gap: 12px; - flex-wrap: wrap; -} - -.compact-row { - gap: 8px; -} - -.search-form { - display: grid; - grid-template-columns: 1fr auto auto; - gap: 10px; - align-items: end; -} - -.filter-row { - grid-template-columns: minmax(260px, 2fr) repeat(3, minmax(140px, 1fr)) auto auto; -} - -.search-form input, -.search-form select, -.form-grid input, -.form-grid textarea, -.form-grid select { - width: 100%; - padding: 10px 11px; - border-radius: 10px; - border: 1px solid var(--line); - background: var(--surface-soft); - color: var(--text); - font: inherit; -} - -.form-grid textarea { - resize: vertical; -} - -.mission-datetime-field, -.mission-datetime-control { - display: grid; - gap: 6px; -} - -.mission-datetime-inputs, -.mission-datetime-control-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; -} - -.mission-datetime-control-row { - align-items: stretch; - position: relative; -} - -.mission-datetime-control-input, -.mission-datetime-display-input { - flex: 1 1 220px; - min-width: 0; -} - -.mission-datetime-display-input { - width: 100%; -} - -.mission-datetime-picker-proxy { - position: absolute; - right: 0; - bottom: 0; - width: 1px !important; - height: 1px !important; - padding: 0 !important; - border: 0 !important; - opacity: 0; - pointer-events: none; -} - -.mission-datetime-picker-button { - padding-inline: 12px; -} - -.mission-datetime-part { - flex: 0 0 62px; - text-align: center; -} - -.mission-datetime-year { - flex-basis: 84px; -} - -.mission-datetime-separator { - color: var(--muted); - font-size: 13px; - font-weight: 700; - line-height: 1; -} - -.combo-custom-input { - margin-top: 8px; -} - -.engine-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - margin-top: 22px; -} - -.engine-card h2 { - margin: 0; - font-size: 24px; -} - -.chip-row { - display: flex; - gap: 10px; - flex-wrap: wrap; - margin: 12px 0; -} - -.chip { - display: inline-flex; - align-items: center; - padding: 6px 10px; - border-radius: 999px; - background: var(--primary-soft); - color: var(--primary-strong); - font-size: 13px; - font-weight: 600; -} - -.chip.strong { - background: var(--primary-soft-strong); - color: var(--primary-strong); -} - -.small-muted { - color: var(--muted); -} - -.preview-list { - display: grid; - gap: 10px; - margin: 16px 0 4px; -} - -.preview-row { - display: grid; - grid-template-columns: 1.5fr 1fr 1fr; - gap: 10px; - padding: 12px 14px; - border-radius: 14px; - background: var(--surface-soft); - border: 1px solid var(--line); -} - -.muted-row { - color: var(--muted); -} - -.empty-state { - text-align: center; -} - -.detail-grid { - grid-template-columns: 0.9fr 1.4fr; - margin-top: 22px; -} - -.summary-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.summary-card { - padding: 16px; - border-radius: 16px; - background: var(--surface-strong); - border: 1px solid var(--line); -} - -.summary-label { - display: block; - margin-bottom: 6px; - color: var(--muted); - font-size: 13px; -} - -.table-shell { - overflow-x: auto; -} - -.data-table { - width: 100%; - border-collapse: collapse; -} - -.compact-table { - table-layout: fixed; -} - -.compact-table td, -.compact-table th { - padding: 7px 8px; -} - -.compact-table td { - font-size: 12.5px; - line-height: 1.3; -} - -.compact-table th { - font-size: 13px; -} - -.table-subline { - margin-top: 1px; - color: var(--muted); - font-size: 12px; -} - -.table-title { - display: block; - font-size: 13px; - line-height: 1.18; -} - -.two-line-cell { - min-width: 190px; - max-width: 240px; -} - -.single-line-cell { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.clamp-line { - display: -webkit-box; - overflow: hidden; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - word-break: break-word; -} - -.column-sort-head { - display: flex; - align-items: center; - justify-content: flex-start; - gap: 6px; - min-width: 0; - width: 100%; -} - -.numeric-head { - display: grid; - justify-items: end; - align-content: center; - gap: 2px; -} - -.sort-chip-group { - display: inline-flex; - gap: 4px; -} - -.sort-chip { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 17px; - padding: 1px 4px; - border-radius: 999px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.75); - color: var(--muted); - text-decoration: none; - font-size: 9px; - font-weight: 700; - line-height: 1.1; -} - -.sort-chip.active { - background: rgba(23, 103, 213, 0.14); - color: var(--primary-strong); - border-color: rgba(23, 103, 213, 0.28); -} - -.table-actions { - display: inline-flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.inline-form { - display: inline-flex; - margin: 0; -} - -.table-link { - color: var(--primary-strong); - font-weight: 700; - text-decoration: none; -} - -.danger-link { - border: 1px solid rgba(182, 52, 57, 0.25); - background: rgba(182, 52, 57, 0.08); - color: var(--danger); - border-radius: 999px; - padding: 6px 10px; - font: inherit; - font-weight: 700; - cursor: pointer; - text-decoration: none; -} - -.pagination-bar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 14px; - margin-top: 18px; - flex-wrap: wrap; -} - -.pagination-pages { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.pagination-link { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 42px; - padding: 10px 14px; - border-radius: 999px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.72); - color: var(--text); - text-decoration: none; - font-weight: 600; -} - -.pagination-link.current { - background: linear-gradient(135deg, var(--primary), var(--primary-strong)); - color: white; - border-color: transparent; -} - -.pagination-link.disabled { - color: var(--muted); - opacity: 0.6; -} - -.pagination-ellipsis { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 32px; - color: var(--muted); -} - -.compact-empty { - padding: 26px 8px 8px; -} - -.data-table th, -.data-table td { - padding: 7px 8px; - text-align: left; - border-bottom: 1px solid var(--line); - vertical-align: middle; -} - -.data-table th { - color: var(--muted); - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.05em; -} - -.engine-name-col { - width: 16%; -} - -.cycle-col { - width: 7%; -} - -.fuel-col { - width: 12%; -} - -.mod-col { - width: 9%; -} - -.count-col { - width: 5%; - text-align: center; -} - -.numeric-col { - width: 6%; - text-align: right; - font-variant-numeric: tabular-nums; -} - -.action-col { - width: 15%; - text-align: right; -} - -.action-col .table-actions { - justify-content: flex-end; -} - -.action-col .table-actions, -.sort-chip-group { - white-space: nowrap; -} - -.variant-note { - margin-top: 6px; - color: var(--muted); - font-size: 13px; - line-height: 1.5; -} - -.edit-layout { - display: grid; - gap: 14px; - margin-top: 14px; -} - -.form-grid { - display: grid; - gap: 14px; -} - -.family-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.variant-editor-stack { - display: grid; - gap: 16px; -} - -.variant-editor { - padding: 18px; - border-radius: 20px; - background: rgba(255, 255, 255, 0.62); - border: 1px solid var(--line); -} - -.variant-editor-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - margin-top: 14px; -} - -.variant-add-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} - -.resource-form-grid { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.field-span-three { - grid-column: span 3; -} - -.field-span-two { - grid-column: span 2; -} - -.checkbox-row { - display: flex; - align-items: center; - gap: 10px; -} - -.checkbox-row input { - width: auto; -} - -.converter-form { - display: grid; - gap: 12px; -} - -.field-label { - font-weight: 600; -} - -.field-hint { - display: block; - margin-top: 6px; - color: var(--muted); - font-size: 12px; - line-height: 1.45; -} - -.converter-form input, -.converter-form select { - width: 100%; - padding: 14px 16px; - border-radius: 14px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.72); - color: var(--text); - font: inherit; -} - -.primary-button { - padding: 9px 13px; - border: 0; - border-radius: 999px; - background: linear-gradient(135deg, var(--primary), var(--primary-strong)); - color: white; - font: inherit; - font-weight: 700; - cursor: pointer; -} - -.primary-button, -.secondary-button, -.danger-link { - white-space: nowrap; -} - -.danger-panel { - border-color: rgba(182, 52, 57, 0.28); - background: rgba(255, 244, 245, 0.9); -} - -.form-error { - margin: 0; - color: var(--warning); - font-weight: 600; -} - -.result-summary { - margin-bottom: 14px; - color: var(--muted); -} - -.result-table { - display: grid; - gap: 10px; -} - -.result-row { - display: grid; - grid-template-columns: 1fr auto auto; - gap: 12px; - align-items: center; - padding: 14px 16px; - border-radius: 14px; - background: rgba(255, 255, 255, 0.72); - border: 1px solid var(--line); -} - -.factor-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} - -.factor-card { - padding: 16px; - border-radius: 16px; - background: var(--surface-strong); - border: 1px solid var(--line); -} - -.mission-hero-side { - display: grid; - gap: 10px; -} - -.mission-time-callout { - padding: 18px; -} - -.mission-time-value { - margin: 8px 0 0; - font-size: 30px; - font-weight: 700; - line-height: 1; - color: var(--primary-strong); -} - -.mission-preview-nav { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-top: 16px; -} - -.mission-preview-nav-top { - margin-top: 0; -} - -.mission-page-header { - display: grid; - grid-template-columns: minmax(220px, 0.78fr) minmax(0, 1.45fr) auto; - gap: 12px; - align-items: center; - padding: 12px 16px; -} - -.mission-page-header-copy { - min-width: 0; -} - -.mission-page-header-copy h1 { - margin: 0; - font-size: clamp(26px, 2.3vw, 36px); - line-height: 1.05; -} - -.mission-page-header-metrics { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 8px; - min-width: 0; -} - -.mission-page-header-controls { - min-width: 0; - justify-self: end; - width: min(100%, 380px); -} - -.mission-page-time-form { - display: flex; - gap: 8px; - align-items: center; - justify-content: flex-end; - width: 100%; -} - -.mission-page-inline-datetime { - display: grid; - grid-template-columns: 64px minmax(160px, 1fr); - gap: 8px; - align-items: center; - margin: 0; - flex: 1 1 auto; -} - -.mission-page-inline-datetime .field-label { - margin: 0; - white-space: nowrap; - text-align: right; -} - -.mission-page-inline-datetime .mission-datetime-control-row { - min-width: 0; -} - -.mission-page-inline-datetime .mission-datetime-display-input { - flex: 1 1 160px; -} - -.mission-summary-card { - padding: 10px 12px; - border-radius: 14px; - background: rgba(255, 255, 255, 0.76); - min-height: 62px; - display: grid; - align-content: space-between; -} - -.mission-summary-card strong { - font-size: clamp(18px, 1.45vw, 24px); - line-height: 1.05; -} - -.mission-summary-card-time strong { - font-size: clamp(15px, 0.95vw, 18px); - color: var(--primary-strong); - white-space: nowrap; -} - -.mission-ops-tabs-panel { - padding: 10px 12px; -} - -.preview-tab { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 8px 12px; - border-radius: 999px; - border: 1px solid var(--line); - background: var(--surface-soft); - color: var(--text); - text-decoration: none; - font-weight: 600; - transition: background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease, color 180ms ease; -} - -.preview-tab:hover, -.preview-tab:focus-visible { - border-color: var(--primary-line); - background: color-mix(in srgb, var(--surface-soft) 84%, var(--primary-soft)); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 10%, transparent); -} - -.preview-tab:focus-visible { - outline: none; -} - -.preview-tab.active { - background: linear-gradient(135deg, var(--primary), var(--primary-strong)); - color: white; - border-color: transparent; -} - -.preview-tab.active:hover, -.preview-tab.active:focus-visible { - border-color: transparent; - box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent); -} - -.mission-metric-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; -} - -.mission-page-time-form .secondary-button { - white-space: nowrap; -} - -.mission-control-panel { - padding: 12px 14px; -} - -.mission-toolbar-grid { - display: grid; - grid-template-columns: minmax(280px, 0.9fr) minmax(0, 1.25fr); - gap: 16px; - align-items: end; -} - -.mission-toolbar-block { - display: grid; - gap: 8px; -} - -.mission-toolbar-span-two { - grid-column: 1 / -1; -} - -.mission-toolbar-inline { - display: flex; - gap: 10px; - flex-wrap: wrap; - align-items: center; -} - -.mission-control-panel input, -.mission-control-panel select { - width: 100%; - padding: 10px 11px; - border-radius: 10px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.72); - color: var(--text); - font: inherit; -} - -.mission-toolbar-inline input { - flex: 1 1 220px; -} - -.mission-toolbar-inline .mission-datetime-control { - min-width: min(280px, 100%); -} - -.mission-filter-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)) auto auto; - gap: 10px; - align-items: end; -} - -.mission-filter-pill-row { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-top: 12px; -} - -.mission-filter-pill { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - border-radius: 999px; - background: rgba(23, 103, 213, 0.08); - border: 1px solid rgba(23, 103, 213, 0.14); - color: var(--primary-strong); - font-size: 12px; - font-weight: 600; -} - -.mission-asset-picker-grid { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; -} - -.mission-asset-picker { - display: grid; - gap: 10px; -} - -.mission-asset-picker-toolbar { - display: grid; - grid-template-columns: minmax(220px, 1fr) auto; - gap: 10px; - align-items: end; -} - -.mission-asset-picker-search { - display: grid; - gap: 6px; -} - -.mission-asset-picker-search input { - width: 100%; - padding: 10px 11px; - border-radius: 10px; - border: 1px solid var(--line); - background: var(--surface-soft); - color: var(--text); - font: inherit; -} - -.mission-asset-picker-search input::placeholder { - color: color-mix(in srgb, var(--muted) 72%, transparent); -} - -.mission-asset-picker-actions { - display: flex; - gap: 8px; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; -} - -.mission-asset-picker-item { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px 12px; - border: 1px solid var(--line); - border-radius: 12px; - background: var(--surface-soft); - color: var(--text); - cursor: pointer; - transition: background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease, transform 180ms ease; -} - -.mission-asset-picker-item:hover { - border-color: var(--primary-line); - background: color-mix(in srgb, var(--surface-soft) 84%, var(--primary-soft)); -} - -.mission-asset-picker-item:has(input:checked) { - border-color: var(--primary-line-strong); - background: color-mix(in srgb, var(--surface-soft) 74%, var(--primary-soft-strong)); - box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); -} - -.mission-asset-picker-item input { - width: auto; - margin: 2px 0 0; - accent-color: var(--primary); -} - -.mission-asset-picker-item strong, -.mission-asset-picker-item small { - display: block; -} - -.mission-asset-picker-item strong { - color: var(--text); -} - -.mission-asset-picker-item small { - margin-top: 2px; - color: var(--muted); - font-size: 11px; -} - -.mission-board-shell, -.mission-detail-layout { - display: grid; - grid-template-columns: minmax(0, 1.45fr) 340px; - gap: 16px; - align-items: start; -} - -.mission-board-main, -.mission-board-side, -.mission-detail-main, -.mission-detail-side { - display: grid; - gap: 14px; -} - -.mission-group-panel, -.mission-log-panel, -.mission-asset-overview, -.mission-timeline-panel { - display: grid; - gap: 14px; -} - -.mission-log-filter-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)) auto auto; - gap: 10px; - align-items: end; -} - -.mission-log-filter-grid input { - width: 100%; - padding: 10px 11px; - border-radius: 10px; - border: 1px solid var(--line); - background: rgba(255, 255, 255, 0.72); - color: var(--text); - font: inherit; -} - -.mission-log-table td { - vertical-align: top; -} - -.mission-log-table .chip { - font-size: 11px; - padding: 5px 8px; -} - -.mission-entry-label-row { - gap: 6px; -} - -.mission-group-header, -.mission-card-top, -.mission-log-top, -.mission-asset-head, -.mission-timeline-toolbar { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: flex-start; - flex-wrap: wrap; -} - -.mission-group-header h2, -.mission-card-top h3, -.mission-asset-head h2, -.mission-upcoming-item h3, -.mission-log-body h3, -.mission-timeline-label h3 { - margin: 0; -} - -.mission-group-count { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 7px 10px; - border-radius: 999px; - background: rgba(255, 255, 255, 0.82); - border: 1px solid var(--line); - color: var(--muted); - font-size: 12px; - font-weight: 700; -} - -.mission-group-subtitle, -.mission-type { - margin: 4px 0 0; -} - -.mission-card-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; -} - -html[data-theme="dark"] .page-shell { - background-image: - linear-gradient(to bottom, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)); -} - -html[data-theme="dark"] .site-nav a, -html[data-theme="dark"] .secondary-button, -html[data-theme="dark"] .mission-datetime-picker-button, -html[data-theme="dark"] .mission-asset-picker-search input, -html[data-theme="dark"] .mission-asset-picker-item, -html[data-theme="dark"] .flash-message, -html[data-theme="dark"] .preview-row, -html[data-theme="dark"] .mission-asset-card, -html[data-theme="dark"] .mission-upcoming-item, -html[data-theme="dark"] .mission-log-body, -html[data-theme="dark"] .legend-item, -html[data-theme="dark"] .state-node-row, -html[data-theme="dark"] .mission-timeline-label, -html[data-theme="dark"] .catalog-sidebar, -html[data-theme="dark"] .catalog-panel, -html[data-theme="dark"] .filter-panel, -html[data-theme="dark"] .panel, -html[data-theme="dark"] .metric-card, -html[data-theme="dark"] .summary-card, -html[data-theme="dark"] .module-card { - background: var(--surface-soft); -} - -html[data-theme="dark"] .hero-panel { - background: linear-gradient(135deg, var(--surface-hero-start), var(--surface-hero-end)); -} - -html[data-theme="dark"] .filter-form input, -html[data-theme="dark"] .filter-form select, -html[data-theme="dark"] .search-form input, -html[data-theme="dark"] .search-form select, -html[data-theme="dark"] input[type="search"], -html[data-theme="dark"] .form-grid input, -html[data-theme="dark"] .form-grid textarea, -html[data-theme="dark"] .form-grid select, -html[data-theme="dark"] input[type="datetime-local"], -html[data-theme="dark"] input[type="text"], -html[data-theme="dark"] input[type="number"], -html[data-theme="dark"] input[type="date"], -html[data-theme="dark"] input[type="time"], -html[data-theme="dark"] select, -html[data-theme="dark"] textarea { - background: var(--surface-soft-strong); - color: var(--text); - border-color: var(--line); -} - -html[data-theme="dark"] input::placeholder, -html[data-theme="dark"] textarea::placeholder { - color: color-mix(in srgb, var(--muted) 82%, transparent); -} - -html[data-theme="dark"] .data-table th, -html[data-theme="dark"] .data-table td, -html[data-theme="dark"] .catalog-table th, -html[data-theme="dark"] .catalog-table td { - border-color: var(--line); -} - -html[data-theme="dark"] .data-table thead th, -html[data-theme="dark"] .catalog-table thead th { - background: color-mix(in srgb, var(--surface-highlight) 80%, transparent); -} - -html[data-theme="dark"] .mission-tone-primary { - background: linear-gradient(180deg, rgba(52, 96, 171, 0.38), rgba(19, 31, 50, 0.9)); -} - -html[data-theme="dark"] .mission-tone-warning { - background: linear-gradient(180deg, rgba(39, 76, 140, 0.4), rgba(19, 31, 50, 0.9)); -} - -html[data-theme="dark"] .mission-tone-neutral { - background: linear-gradient(180deg, rgba(55, 66, 84, 0.38), rgba(19, 31, 50, 0.9)); -} - -html[data-theme="dark"] .mission-tone-soft { - background: rgba(20, 30, 46, 0.82); -} - -html[data-theme="dark"] .mission-status-pill, -html[data-theme="dark"] .chip, -html[data-theme="dark"] .mission-segment, -html[data-theme="dark"] .hero-callout { - background: var(--primary-soft); - border-color: var(--primary-line); -} - -html[data-theme="dark"] .chip.strong, -html[data-theme="dark"] .mission-tone-warning .mission-segment-body, -html[data-theme="dark"] .mission-tone-primary .mission-segment-body, -html[data-theme="dark"] .mission-tone-current.mission-segment { - background: var(--primary-soft-strong); -} - -html[data-theme="dark"] .mission-tone-primary.mission-segment, -html[data-theme="dark"] .mission-status-pill, -html[data-theme="dark"] .chip, -html[data-theme="dark"] .chip.strong, -html[data-theme="dark"] .mission-segment, -html[data-theme="dark"] .module-status, -html[data-theme="dark"] .mission-upcoming-date, -html[data-theme="dark"] .mission-log-kicker, -html[data-theme="dark"] .eyebrow { - color: var(--primary-strong); -} - -html[data-theme="dark"] .mission-log-rail::before { - background: color-mix(in srgb, var(--muted) 36%, transparent); -} - -html[data-theme="dark"] .mission-asset-picker-item:hover { - border-color: var(--primary-line-strong); - background: color-mix(in srgb, var(--surface-soft-strong) 78%, var(--primary-soft)); -} - -html[data-theme="dark"] .mission-asset-picker-item:has(input:checked) { - background: color-mix(in srgb, var(--surface-soft-strong) 70%, var(--primary-soft-strong)); - box-shadow: 0 0 0 3px rgba(96, 149, 255, 0.14); -} - -html[data-theme="dark"] .mission-log-dot { - border-color: rgba(12, 19, 31, 0.94); - box-shadow: 0 0 0 2px rgba(96, 149, 255, 0.14); -} - -html[data-theme="dark"] .mission-log-entry.state .mission-log-dot, -html[data-theme="dark"] .mission-event-node-state::before { - background: #8ea8d9; -} - -html[data-theme="dark"] .mission-timeline-corner, -html[data-theme="dark"] .state-node-panel { - background: linear-gradient(180deg, var(--surface-panel-start), var(--surface-panel-end)); -} - -html[data-theme="dark"] .mission-timeline-row { - border-bottom-color: color-mix(in srgb, var(--line) 70%, transparent); -} - -html[data-theme="dark"] .mission-timeline-label { - background: var(--surface-highlight); - box-shadow: var(--timeline-label-shadow); -} - -html[data-theme="dark"] .mission-timeline-label-link:hover, -html[data-theme="dark"] .mission-timeline-label-link:focus-visible { - border-color: var(--primary-line-strong); - box-shadow: var(--timeline-label-shadow-active); -} - -html[data-theme="dark"] .mission-timeline-lane { - background: - repeating-linear-gradient( - to right, - var(--timeline-grid) 0, - var(--timeline-grid) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(20, 30, 47, 0.88), rgba(13, 21, 34, 0.92)); -} - -html[data-theme="dark"] .mission-timeline-state-lane { - background: - repeating-linear-gradient( - to right, - var(--timeline-grid) 0, - var(--timeline-grid) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(21, 33, 51, 0.88), rgba(14, 22, 35, 0.92)); -} - -html[data-theme="dark"] .mission-timeline-event-lane { - background: - repeating-linear-gradient( - to right, - var(--timeline-grid-soft) 0, - var(--timeline-grid-soft) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(25, 37, 57, 0.92), rgba(16, 24, 38, 0.94)); -} - -html[data-theme="dark"] .mission-segment-body { - background: var(--primary-soft-strong); - border-right-color: color-mix(in srgb, var(--primary) 36%, transparent); -} - -html[data-theme="dark"] .mission-tone-neutral.mission-segment, -html[data-theme="dark"] .mission-tone-soft.mission-segment { - background: var(--neutral-soft); - border-color: color-mix(in srgb, var(--muted) 30%, transparent); - color: #dbe6fa; -} - -html[data-theme="dark"] .mission-tone-neutral .mission-segment-body, -html[data-theme="dark"] .mission-tone-soft .mission-segment-body { - background: var(--neutral-soft-strong); -} - -html[data-theme="dark"] .mission-event-node::before { - box-shadow: 0 0 0 2px rgba(7, 12, 21, 0.98), 0 0 0 3px rgba(96, 149, 255, 0.1); -} - -html[data-theme="dark"] .mission-event-node-label, -html[data-theme="dark"] .legend-item, -html[data-theme="dark"] .table-subline, -html[data-theme="dark"] .mission-summary-text, -html[data-theme="dark"] .mission-card-foot, -html[data-theme="dark"] .hero-text, -html[data-theme="dark"] .panel p, -html[data-theme="dark"] .module-card p { - color: var(--muted); -} - -html[data-theme="dark"] .mission-state-node-connector { - border-left-color: color-mix(in srgb, var(--muted) 42%, transparent); -} - -html[data-theme="dark"] .mission-segment:hover::after, -html[data-theme="dark"] .mission-segment:focus-visible::after, -html[data-theme="dark"] .mission-event-node:hover::after, -html[data-theme="dark"] .mission-event-node:focus-visible::after { - background: var(--tooltip-bg); - color: var(--tooltip-text); - border: 1px solid color-mix(in srgb, var(--line) 90%, transparent); -} - -html[data-theme="dark"] .flash-message.error, -html[data-theme="dark"] .warning-box, -html[data-theme="dark"] .field-error { - background: var(--danger-soft); - border-color: var(--danger-line); -} - -html[data-theme="dark"] input:focus-visible, -html[data-theme="dark"] select:focus-visible, -html[data-theme="dark"] textarea:focus-visible, -html[data-theme="dark"] button:focus-visible, -html[data-theme="dark"] a:focus-visible { - outline: none; - box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 18%, transparent); -} - -@media (max-width: 1120px) { - .site-header { - flex-direction: column; - align-items: flex-start; - } - - .site-header-tools { - width: 100%; - justify-content: flex-start; - } -} - -@media (max-width: 760px) { - .site-header-tools { - gap: 10px; - } - - .theme-toggle { - width: 100%; - justify-content: center; - } -} - -.mission-asset-card { - display: grid; - gap: 12px; - padding: 16px; - border-radius: 20px; - background: rgba(255, 255, 255, 0.72); - border: 1px solid var(--line); -} - -.mission-tone-primary { - background: linear-gradient(180deg, rgba(219, 235, 255, 0.86), rgba(255, 255, 255, 0.82)); -} - -.mission-tone-warning { - background: linear-gradient(180deg, rgba(231, 242, 255, 0.96), rgba(255, 255, 255, 0.82)); -} - -.mission-tone-neutral { - background: linear-gradient(180deg, rgba(245, 248, 252, 0.96), rgba(255, 255, 255, 0.84)); -} - -.mission-tone-soft { - background: rgba(255, 255, 255, 0.76); -} - -.mission-status-pill { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 6px 10px; - border-radius: 999px; - background: rgba(23, 103, 213, 0.1); - border: 1px solid rgba(23, 103, 213, 0.15); - color: var(--primary-strong); - font-size: 11px; - font-weight: 700; - letter-spacing: 0.04em; - text-transform: uppercase; -} - -.mission-meta-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px 12px; -} - -.mission-meta-item { - margin: 0; -} - -.mission-meta-item dt { - margin: 0; - color: var(--muted); - font-size: 11px; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; -} - -.mission-meta-item dd { - margin: 5px 0 0; - font-weight: 600; - line-height: 1.45; -} - -.mission-summary-text { - margin: 0; - color: var(--muted); - line-height: 1.55; -} - -.mission-card-foot { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - flex-wrap: wrap; - padding-top: 10px; - border-top: 1px dashed var(--line); - color: var(--muted); - font-size: 12px; -} - -.mission-upcoming-list, -.mission-action-stack { - display: grid; - gap: 10px; -} - -.mission-future-panel, -.mission-future-list { - display: grid; - gap: 12px; -} - -.mission-upcoming-item { - padding: 12px 13px; - border-radius: 16px; - background: rgba(255, 255, 255, 0.74); - border: 1px solid var(--line); -} - -.mission-upcoming-head { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: flex-start; -} - -.mission-future-action { - white-space: nowrap; -} - -.mission-future-summary { - margin: 6px 0 0; -} - -.mission-upcoming-date { - margin: 0; - color: var(--primary); - font-size: 12px; - font-weight: 700; - letter-spacing: 0.06em; -} - -.mission-prototype-list { - display: grid; - gap: 10px; - margin: 0; - padding-left: 18px; - color: var(--muted); - line-height: 1.55; -} - -.mission-asset-chip-row { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.mission-log-list { - display: grid; - gap: 12px; -} - -.mission-log-entry { - display: grid; - grid-template-columns: 34px minmax(0, 1fr); - gap: 12px; -} - -.mission-log-rail { - position: relative; - display: flex; - justify-content: center; -} - -.mission-log-rail::before { - content: ""; - position: absolute; - top: 14px; - bottom: -14px; - width: 2px; - background: rgba(18, 35, 62, 0.1); -} - -.mission-log-dot { - position: relative; - z-index: 1; - width: 14px; - height: 14px; - margin-top: 6px; - border-radius: 999px; - background: var(--primary-strong); - border: 3px solid rgba(255, 255, 255, 0.92); - box-shadow: 0 0 0 2px rgba(23, 103, 213, 0.16); -} - -.mission-log-entry.state .mission-log-dot { - background: #3e6fb9; -} - -.mission-log-body { - display: grid; - gap: 8px; - padding: 14px 16px; - border-radius: 18px; - background: rgba(255, 255, 255, 0.74); - border: 1px solid var(--line); -} - -.mission-log-kicker { - color: var(--primary); - font-size: 11px; - font-weight: 700; - letter-spacing: 0.18em; - text-transform: uppercase; -} - -.mission-log-time { - margin: 0; - color: var(--muted); - font-size: 13px; -} - -.mission-action-stack .primary-button, -.mission-action-stack .secondary-button { - width: 100%; -} - -.mission-legend { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.legend-item { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 6px 10px; - border-radius: 999px; - background: rgba(255, 255, 255, 0.8); - border: 1px solid var(--line); - color: var(--muted); - font-size: 12px; - font-weight: 600; -} - -.legend-swatch { - width: 12px; - height: 12px; - border-radius: 999px; - display: inline-block; -} - -.legend-swatch.warning { - background: rgba(23, 103, 213, 0.36); -} - -.legend-swatch.primary { - background: rgba(13, 79, 168, 0.42); -} - -.legend-swatch.neutral { - background: rgba(91, 111, 143, 0.36); -} - -.legend-swatch.current { - background: rgba(23, 103, 213, 0.78); - box-shadow: 0 0 0 2px rgba(23, 103, 213, 0.16); -} - -.mission-timeline-scroll { - overflow-x: auto; -} - -.mission-timeline-frame { - min-width: 1120px; -} - -.mission-timeline-scale { - display: grid; - grid-template-columns: 220px minmax(0, 1fr); - gap: 12px; - margin-bottom: 6px; -} - -.mission-timeline-corner { - position: sticky; - left: 0; - z-index: 5; - background: linear-gradient(180deg, rgba(245, 249, 255, 0.98), rgba(240, 246, 255, 0.98)); - border-radius: 16px; -} - -.mission-scale-labels { - display: grid; - grid-template-columns: repeat(var(--timeline-columns, 8), minmax(var(--timeline-column-width, 72px), 1fr)); - gap: 8px; -} - -.mission-scale-labels span { - padding: 0 0 6px; - color: var(--muted); - font-size: 12px; - font-weight: 700; - text-align: center; -} - -.mission-timeline-rows { - display: grid; - gap: 10px; -} - -.mission-timeline-row { - display: grid; - grid-template-columns: 220px minmax(0, 1fr); - gap: 12px; - align-items: start; - padding-bottom: 10px; - border-bottom: 1px solid rgba(18, 35, 62, 0.08); -} - -.mission-timeline-row:last-child { - padding-bottom: 0; - border-bottom: 0; -} - -.mission-timeline-label { - position: sticky; - left: 0; - z-index: 4; - display: block; - padding: 12px 14px; - border-radius: 18px; - background: rgba(248, 251, 255, 0.96); - border: 1px solid var(--line); - box-shadow: 14px 0 18px -18px rgba(18, 35, 62, 0.28); -} - -.mission-timeline-label-link { - color: inherit; - text-decoration: none; - transition: border-color 120ms ease, box-shadow 120ms ease, transform 120ms ease; -} - -.mission-timeline-label-link:hover, -.mission-timeline-label-link:focus-visible { - border-color: rgba(23, 103, 213, 0.24); - box-shadow: 14px 0 20px -18px rgba(18, 35, 62, 0.32), 0 0 0 3px rgba(23, 103, 213, 0.08); -} - -.mission-timeline-label-link:focus-visible { - outline: none; -} - -.mission-timeline-label p { - margin: 4px 0 0; -} - -.mission-timeline-meta, -.mission-timeline-status { - font-size: 11px; - line-height: 1.35; -} - -.mission-timeline-status { - margin-top: 2px; -} - -.state-node-panel { - display: grid; - gap: 14px; - padding: 16px 18px; - border-radius: 18px; - border: 1px solid var(--line); - background: linear-gradient(180deg, rgba(248, 251, 255, 0.88), rgba(241, 247, 255, 0.88)); -} - -.state-node-panel-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; -} - -.state-node-panel-header .field-hint, -.state-node-empty { - margin: 6px 0 0; -} - -.state-node-list { - display: grid; - gap: 12px; -} - -.state-node-row { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(260px, 0.95fr) minmax(0, 1.2fr) auto; - gap: 12px; - align-items: end; - padding: 12px 14px; - border-radius: 14px; - border: 1px solid rgba(18, 35, 62, 0.08); - background: rgba(255, 255, 255, 0.82); -} - -.state-node-row-error { - border-color: var(--warning-line); - box-shadow: 0 0 0 3px rgba(214, 138, 38, 0.12); -} - -.state-node-row label, -.state-node-row .mission-datetime-control { - margin: 0; -} - -.state-node-detail-field textarea { - min-height: 72px; - resize: vertical; -} - -.state-node-datetime { - min-width: 0; -} - -.state-node-remove-button { - align-self: end; -} - -.mission-timeline-track-stack { - display: grid; - gap: 8px; -} - -.mission-timeline-lane { - position: relative; - min-height: calc(var(--timeline-track-count, 1) * var(--timeline-track-step, 38px) + var(--timeline-track-base, 24px)); - border-radius: 18px; - background: - repeating-linear-gradient( - to right, - rgba(18, 35, 62, 0.08) 0, - rgba(18, 35, 62, 0.08) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(238, 245, 255, 0.72)); - border: 1px solid var(--line); - overflow: visible; -} - -.mission-timeline-state-lane { - --timeline-track-step: 38px; - --timeline-track-base: 22px; - background: - repeating-linear-gradient( - to right, - rgba(18, 35, 62, 0.08) 0, - rgba(18, 35, 62, 0.08) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(255, 255, 255, 0.74), rgba(237, 244, 255, 0.8)); -} - -.mission-timeline-event-lane { - --timeline-track-step: 54px; - --timeline-track-base: 18px; - background: - repeating-linear-gradient( - to right, - rgba(18, 35, 62, 0.06) 0, - rgba(18, 35, 62, 0.06) 1px, - transparent 1px, - transparent calc(100% / var(--timeline-columns, 1)) - ), - linear-gradient(180deg, rgba(251, 253, 255, 0.92), rgba(244, 248, 255, 0.88)); -} - -.mission-timeline-lane-year { - border-radius: 20px; -} - -.mission-segment { - position: absolute; - top: calc(10px + var(--timeline-track-index, 0) * 38px); - z-index: 1; - min-height: 30px; - padding: 0 10px; - border-radius: 999px; - font-size: 12px; - font-weight: 700; - color: var(--primary-strong); - background: rgba(23, 103, 213, 0.08); - border: 1px solid rgba(23, 103, 213, 0.16); - cursor: pointer; - text-decoration: none; - overflow: hidden; -} - -.mission-timeline-state-lane .mission-segment { - top: calc(10px + var(--timeline-track-index, 0) * var(--timeline-track-step, 38px)); -} - -.mission-segment-compact { - padding: 0 7px; -} - -.mission-segment-compact .mission-segment-text { - font-size: 10px; -} - -.mission-segment-body { - position: absolute; - inset: 0 auto 0 0; - min-width: 8px; - border-radius: 999px; - background: rgba(23, 103, 213, 0.16); - border-right: 1px solid rgba(23, 103, 213, 0.12); -} - -.mission-segment-text { - position: relative; - z-index: 1; - display: block; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.mission-tone-warning.mission-segment { - background: rgba(23, 103, 213, 0.09); -} - -.mission-tone-warning .mission-segment-body { - background: rgba(23, 103, 213, 0.16); -} - -.mission-tone-primary.mission-segment { - background: rgba(13, 79, 168, 0.1); - color: #0a3b7d; -} - -.mission-tone-primary .mission-segment-body { - background: rgba(13, 79, 168, 0.2); -} - -.mission-tone-neutral.mission-segment, -.mission-tone-soft.mission-segment { - background: rgba(91, 111, 143, 0.08); - border-color: rgba(91, 111, 143, 0.18); - color: #42546d; -} - -.mission-tone-neutral .mission-segment-body, -.mission-tone-soft .mission-segment-body { - background: rgba(91, 111, 143, 0.16); -} - -.mission-tone-current.mission-segment { - box-shadow: 0 0 0 2px rgba(23, 103, 213, 0.18); -} - -.mission-event-node { - display: flex; - align-items: flex-end; - justify-content: flex-start; - width: 0; - min-width: 0; - min-height: 48px; - cursor: pointer; - text-decoration: none; - position: absolute; - top: calc(6px + var(--timeline-track-index, 0) * var(--timeline-track-step, 54px)); - z-index: 2; - white-space: nowrap; - overflow: visible; -} - -.mission-event-node::before { - content: ""; - position: absolute; - left: -5px; - top: 12px; - width: 10px; - height: 10px; - border-radius: 999px; - background: var(--primary-strong); - box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.96), 0 0 0 4px rgba(23, 103, 213, 0.14); - z-index: 1; -} - -.mission-event-node-label { - position: absolute; - left: 10px; - bottom: 0; - display: block; - width: var(--event-label-width, 110px); - color: var(--muted); - font-size: 11px; - line-height: 1.35; - text-align: left; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - z-index: 1; -} - -.mission-event-node-label-left .mission-event-node-label { - left: auto; - right: 10px; - text-align: right; -} - -.mission-event-node-state::before { - background: #5b6f8f; - box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.96), 0 0 0 4px rgba(91, 111, 143, 0.16); -} - -.mission-state-node-connector { - position: absolute; - left: 0; - top: calc(17px - var(--state-node-connector-height, 0px)); - height: var(--state-node-connector-height, 0px); - border-left: 2px dashed rgba(91, 111, 143, 0.6); - pointer-events: none; - z-index: 0; -} - -.mission-segment:hover, -.mission-event-node:hover { - z-index: 3; -} - -.mission-segment:hover::after, -.mission-segment:focus-visible::after, -.mission-event-node:hover::after, -.mission-event-node:focus-visible::after { - content: attr(data-full-label); - position: absolute; - top: calc(100% + 8px); - left: 0; - max-width: min(320px, 42vw); - padding: 8px 10px; - border-radius: 10px; - background: rgba(18, 35, 62, 0.96); - color: #fff; - font-size: 12px; - font-weight: 600; - line-height: 1.45; - white-space: normal; - box-shadow: var(--shadow); - pointer-events: none; - z-index: 5; -} - -.mission-event-node:hover::after, -.mission-event-node:focus-visible::after { - top: auto; - bottom: calc(100% + 10px); - left: 50%; - transform: translateX(-50%); -} - -@media (max-width: 1180px) { - .mission-hero, - .mission-toolbar-grid, - .mission-board-shell, - .mission-detail-layout, - .mission-card-grid { - grid-template-columns: 1fr; - } - - .mission-filter-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .mission-asset-picker-toolbar { - grid-template-columns: 1fr; - } - - .mission-asset-picker-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .mission-log-filter-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .state-node-row { - grid-template-columns: 1fr; - } - - .mission-hero-side, - .mission-board-side, - .mission-detail-side { - position: static; - } -} - -@media (max-width: 820px) { - .mission-page-header { - grid-template-columns: minmax(220px, 0.75fr) minmax(0, 1fr); - } - - .state-node-panel-header { - flex-direction: column; - } - - .state-node-remove-button { - width: 100%; - } - - .mission-page-header-copy { - grid-column: 1; - grid-row: 1 / span 2; - } - - .mission-page-header-metrics, - .mission-page-header-controls { - grid-column: 2; - } - - .mission-page-header-metrics { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 1120px) { - .mission-metric-grid, - .mission-meta-grid, - .mission-filter-grid, - .mission-asset-picker-grid, - .mission-card-grid, - .mission-timeline-row, - .mission-timeline-scale { - grid-template-columns: 1fr; - } - - .mission-toolbar-inline, - .mission-card-foot { - flex-direction: column; - align-items: flex-start; - } - - .mission-page-header { - grid-template-columns: 1fr; - } - - .mission-page-header-copy, - .mission-page-header-metrics, - .mission-page-header-controls { - grid-column: auto; - grid-row: auto; - } - - .mission-page-time-form { - flex-wrap: wrap; - justify-content: flex-start; - } - - .mission-page-inline-datetime { - grid-template-columns: 1fr; - } - - .mission-event-node span { - position: static; - transform: none; - width: auto; - margin-top: 24px; - } - - .mission-timeline-label, - .mission-timeline-corner { - position: static; - } -} - -@media (max-width: 960px) { - .hero-panel, - .catalog-layout, - .content-grid, - .metric-grid, - .decision-grid, - .factor-grid, - .engine-grid, - .detail-grid, - .summary-grid, - .family-grid, - .variant-editor-grid, - .variant-add-grid, - .resource-form-grid { - grid-template-columns: 1fr; - } - - .search-form, - .filter-row { - grid-template-columns: 1fr; - } - - .filter-panel { - position: static; - } - - .results-header, - .pagination-bar { - flex-direction: column; - align-items: flex-start; - } - - .catalog-header, - .catalog-page-header { - grid-template-columns: 1fr; - } - - .catalog-metrics { - grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); - width: 100%; - } - - .catalog-header-time-form { - grid-template-columns: minmax(0, 1fr) auto; - } - - .catalog-actions-panel { - flex-direction: column; - align-items: flex-start; - } - - .preview-row { - grid-template-columns: 1fr; - } - - .field-span-two { - grid-column: span 1; - } -} - -@media (max-width: 720px) { - .catalog-header { - grid-template-columns: 1fr; - } - - .catalog-metrics, - .catalog-metrics-duo, - .catalog-header-time-form { - grid-template-columns: 1fr; - width: 100%; - } - - .field-span-three { - grid-column: span 1; - } - - .site-header { - flex-direction: column; - align-items: flex-start; - } - - .hero-copy h1 { - max-width: none; - } -} + --bg:#0B0B10;--surface:#131318;--surface2:#1a1a22;--border:rgba(248,250,252,.06); + --text:#F8FAFC;--muted:#94A3B8;--accent:#3B82F6;--accent-glow:rgba(59,130,246,.3); + --green:#22c55e;--amber:#eab308;--red:#ef4444;--cyan:#06b6d4;--purple:#a855f7; + --radius:6px;--sidebar-w:224px;--topbar-h:42px; + --font-data:'SF Mono','Cascadia Code','Consolas',monospace; + --font-ui:system-ui,-apple-system,'Segoe UI',sans-serif; + color-scheme:dark; +} + +/* ─── Light Theme ─── */ +[data-theme="light"] { + --bg:#F1F5F9;--surface:#FFFFFF;--surface2:#F8FAFC;--border:rgba(0,0,0,.06); + --text:#0F172A;--muted:#64748B;--accent:#2563EB;--accent-glow:rgba(37,99,235,.15); + color-scheme:light; +} + +/* ─── Reset ─── */ +*{margin:0;padding:0;box-sizing:border-box} +body{background:var(--bg);color:var(--text);font-family:var(--font-ui);height:100vh;overflow:hidden;display:flex;flex-direction:column} +a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline} +::-webkit-scrollbar{width:4px;height:4px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px} + +/* ─── Topbar ─── */ +#topbar{height:var(--topbar-h);display:flex;align-items:center;gap:12px;padding:0 14px;background:var(--surface);border-bottom:1px solid var(--border);z-index:30;flex-shrink:0} +.brand{font-family:var(--font-data);font-size:.78rem;font-weight:600;letter-spacing:-.02em}.brand b{color:var(--accent)} +.tb-sp{flex:1} +.tb-time{display:flex;align-items:center;gap:5px} +.tb-time .l{font-size:.62rem;color:var(--muted);text-transform:uppercase;letter-spacing:.05em} +.tb-time .td{font-family:var(--font-data);font-size:.74rem;color:var(--accent);background:rgba(59,130,246,.06);padding:3px 8px;border-radius:4px;cursor:pointer;border:1px solid transparent;transition:.15s} +.tb-time .td:hover{background:rgba(59,130,246,.12);border-color:var(--accent-glow)} + +/* ─── Buttons ─── */ +.b{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:4px;font-family:var(--font-ui);font-size:.7rem;cursor:pointer;border:1px solid var(--border);background:transparent;color:var(--muted);transition:.15s;white-space:nowrap} +.b:hover{background:rgba(128,128,128,.08);color:var(--text)} +.b.ac{background:var(--accent);color:#fff;border-color:var(--accent)}.b.ac:hover{box-shadow:0 0 10px var(--accent-glow)} +.b.sm{padding:2px 6px;font-size:.64rem}.b.dg{border-color:var(--red);color:var(--red)}.b.dg:hover{background:rgba(239,68,68,.08)} + +/* ─── Layout ─── */ +#layout{display:flex;flex:1;overflow:hidden} +#sidebar{width:var(--sidebar-w);flex-shrink:0;background:var(--surface);border-right:1px solid var(--border);overflow-y:auto;padding:6px 0;user-select:none} +#content{flex:1;overflow-y:auto;padding:20px 24px;position:relative} +.pg{display:none}.pg.act{display:block} + +/* ─── Sidebar Navigation ─── */ +.nst{display:flex;align-items:center;gap:6px;padding:8px 14px 2px;font-family:var(--font-data);font-size:.6rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;cursor:pointer} +.nst .ar{font-size:.5rem;transition:transform .15s;opacity:.4} +.nst.on .ar{transform:rotate(-90deg)} +.nst.on+.niw{display:none} +.niw{padding-bottom:2px} +.ni{display:flex;align-items:center;gap:8px;padding:6px 14px;cursor:pointer;font-size:.76rem;color:var(--muted);transition:.1s;border-left:2px solid transparent} +.ni:hover{color:var(--text);background:rgba(128,128,128,.03)} +.ni.act{color:var(--text);background:rgba(59,130,246,.05);border-left-color:var(--accent)} +.ni .ic{width:16px;height:16px;display:flex;align-items:center;justify-content:center;font-size:.8rem;flex-shrink:0;opacity:.6} +.ni.su{padding-left:36px;font-size:.72rem} + +/* ─── Page Header ─── */ +.ph{margin-bottom:18px}.ph .ey{font-family:var(--font-data);font-size:.6rem;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:2px} +.ph h1{font-family:var(--font-data);font-size:1.1rem;font-weight:600}.ph p{color:var(--muted);font-size:.72rem;margin-top:1px} +.ph-row{display:flex;align-items:center;justify-content:space-between} + +/* ─── Metric Cards ─── */ +.mg{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:18px} +.mc{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;transition:border-color .15s} +.mc:hover{border-color:rgba(248,250,252,.1)} +.mc .l{font-family:var(--font-data);font-size:.6rem;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:4px} +.mc .v{font-family:var(--font-data);font-size:1.6rem;font-weight:600}.mc .s{font-size:.66rem;color:var(--muted);margin-top:2px} +.mc.ac .v{color:var(--accent)}.mc.gn .v{color:var(--green)}.mc.am .v{color:var(--amber)} + +/* ─── Status Board ─── */ +.stl{font-family:var(--font-data);font-size:.68rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:8px} +.sgr{display:flex;flex-direction:column;gap:6px;margin-bottom:16px} +.sgi{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden} +.sgh{display:flex;align-items:center;gap:8px;padding:8px 14px;border-bottom:1px solid var(--border);font-family:var(--font-data);font-size:.66rem;font-weight:500;cursor:pointer;user-select:none} +.sdt{width:6px;height:6px;border-radius:50%;flex-shrink:0} +.sdt.tf{background:var(--amber);box-shadow:0 0 5px var(--amber)}.sdt.ex{background:var(--accent);box-shadow:0 0 5px var(--accent-glow)} +.sdt.mt{background:var(--purple);box-shadow:0 0 5px var(--purple)}.sdt.dk{background:var(--green);box-shadow:0 0 5px var(--green)} +.scc{color:var(--muted);font-size:.64rem;margin-left:auto} +.srw{display:flex;align-items:center;gap:10px;padding:5px 14px 5px 30px;font-size:.74rem;transition:background .1s} +.srw:hover{background:rgba(128,128,128,.02)} +.srw .an{color:var(--accent);cursor:pointer;flex:1;font-weight:500}.srw .an:hover{text-decoration:underline} +.srw .am{color:var(--muted);font-size:.66rem}.srw .al{color:var(--muted);font-size:.66rem;font-family:var(--font-data)} + +/* ─── Events ─── */ +.evl{display:flex;flex-direction:column;gap:3px;margin-bottom:18px} +.evr{display:flex;align-items:center;gap:10px;padding:7px 10px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);font-size:.74rem} +.evr .ed{font-family:var(--font-data);font-size:.66rem;color:var(--amber);min-width:76px} +.evr .eb{font-family:var(--font-data);font-size:.6rem;padding:2px 6px;border-radius:3px;background:rgba(234,179,8,.08);color:var(--amber)} + +/* ─── Filter Bar ─── */ +.fb{display:flex;align-items:center;gap:8px;margin-bottom:12px;flex-wrap:wrap} +.fb input[type=text],.fb select,.fb input[type=date]{padding:5px 8px;border-radius:4px;background:var(--surface2);color:var(--text);border:1px solid var(--border);font-size:.72rem} +.fb input[type=text]{width:180px} +.fb input[type=text]:focus,.fb select:focus,.fb input[type=date]:focus{outline:none;border-color:var(--accent)} +.fb label{display:flex;align-items:center;gap:4px;font-size:.7rem;color:var(--muted);cursor:pointer} + +/* ─── Data Tables ─── */ +table{width:100%;border-collapse:collapse;font-size:.74rem} +th,td{padding:6px 10px;text-align:left;border-bottom:1px solid var(--border)} +th{font-family:var(--font-data);font-size:.62rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;cursor:pointer;user-select:none;white-space:nowrap} +th:hover{color:var(--text)} +.th-sa{color:var(--accent);margin-left:2px} +td{color:var(--text)} +td.mt{color:var(--muted);font-size:.68rem} +tr:hover td{background:rgba(128,128,128,.015)} +.act-btn{display:inline-flex;gap:4px;font-size:.68rem} +.act-btn a{color:var(--muted);text-decoration:none;padding:2px 5px;border-radius:3px;transition:.1s} +.act-btn a:hover{background:var(--border);color:var(--text)} + +/* ─── Pagination ─── */ +.pg-bar{display:flex;align-items:center;gap:3px;margin-top:12px;font-size:.7rem} +.pg-bar span,.pg-bar a{padding:3px 7px;border-radius:4px;color:var(--muted);text-decoration:none} +.pg-bar a:hover{background:var(--border);color:var(--text)}.pg-bar .cur{background:var(--accent);color:#fff} + +/* ─── Timeline ─── */ +.tl-r{display:flex;align-items:center;gap:8px;padding:5px 8px;margin-bottom:2px;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);font-size:.72rem} +.tl-bar{flex:1;height:5px;border-radius:3px;background:var(--border);position:relative;overflow:hidden} +.tl-fl{position:absolute;top:0;height:100%;border-radius:3px;opacity:.75} +.tl-fl.warning{background:var(--amber)}.tl-fl.primary{background:var(--accent)}.tl-fl.mt,.tl-fl.neutral{background:var(--purple)}.tl-fl.soft{background:var(--green)}.tl-fl.dk{background:var(--green)} +.tl-fl.warning.current{background:var(--amber);box-shadow:0 0 6px var(--amber)} +.tl-fl.primary.current{background:var(--accent);box-shadow:0 0 6px var(--accent)} +.tl-fl.neutral.current{background:var(--purple);box-shadow:0 0 6px var(--purple)} +.tl-fl.soft.current{background:var(--green);box-shadow:0 0 6px var(--green)} +.tl-scale{display:flex;margin-bottom:6px;font-size:.58rem;color:var(--muted);text-align:center;border-bottom:1px solid var(--border);padding-bottom:4px} +.tl-scale>div{flex:1} + +/* ─── Fuel Converter ─── */ +.cvw{display:flex;gap:16px}.cvw form{display:flex;flex-direction:column;gap:8px;width:260px} +.cvw form label{font-size:.72rem;color:var(--muted)}.cvw form input,.cvw form select{padding:5px 8px;border-radius:4px;background:var(--surface2);color:var(--text);border:1px solid var(--border)} +.cvw .res{flex:1} + +/* ─── Modals ─── */ +.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:100;display:flex;align-items:center;justify-content:center;opacity:0;pointer-events:none;transition:opacity .2s} +.modal-bg.open{opacity:1;pointer-events:auto} +.modal{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;width:400px;max-height:80vh;overflow-y:auto} +.modal h2{font-family:var(--font-data);font-size:.85rem;margin-bottom:14px} +.modal .fg{display:flex;flex-direction:column;gap:8px;margin-bottom:12px} +.modal .fg label{font-size:.72rem;color:var(--muted)} +.modal .fg input,.modal .fg select,.modal .fg textarea{padding:6px 8px;border-radius:4px;background:var(--bg);color:var(--text);border:1px solid var(--border);font-size:.74rem} +.modal .fg textarea{min-height:60px;resize:vertical} +.modal .fg input:focus,.modal .fg select:focus,.modal .fg textarea:focus{outline:none;border-color:var(--accent)} +.modal .btn-row{display:flex;gap:6px;justify-content:flex-end} + +/* ─── Location Board ─── */ +#map-ct{flex:1;position:relative;display:flex;align-items:center;justify-content:center;background:var(--bg);cursor:grab;overflow:hidden} +#map-cv{display:block} +#bk-btn{position:absolute;top:10px;left:10px;z-index:5;display:none;background:var(--surface);border:1px solid var(--border);color:var(--text);padding:4px 12px;border-radius:var(--radius);cursor:pointer;font-size:.74rem} +#bk-btn.vis{display:block} +#scope-ind{position:absolute;top:10px;right:10px;z-index:5;background:rgba(19,19,24,.85);border:1px solid var(--border);color:var(--muted);padding:3px 10px;border-radius:4px;font-size:.68rem;font-family:var(--font-data)} +#map-info{width:280px;background:var(--surface);border-left:1px solid var(--border);padding:16px;overflow-y:auto;display:flex;flex-direction:column;gap:14px;flex-shrink:0} +#map-info h2{font-family:var(--font-data);font-size:.85rem;font-weight:600;padding-bottom:5px;border-bottom:1px solid var(--border)} +.mi-sec h3{font-family:var(--font-data);font-size:.6rem;text-transform:uppercase;color:var(--muted);letter-spacing:.05em;margin-bottom:3px} +.mi-val{font-family:var(--font-data);font-size:1.3rem;font-weight:600;color:var(--accent)} +.mi-arow{display:flex;justify-content:space-between;align-items:center;padding:4px 0;border-bottom:1px solid var(--border);font-size:.76rem} +.mi-arow .n{color:var(--accent);cursor:pointer}.mi-arow .n:hover{text-decoration:underline} +.mi-arow .loc{color:var(--muted);font-size:.68rem;font-family:var(--font-data)} + +/* ─── Location Board Full-Height Page ─── */ +.pg.loc{display:none;position:absolute;inset:0;padding:0 !important;z-index:20;background:var(--bg)} +.pg.loc.act{display:flex;flex-direction:column} +#tl-layout{flex:1;display:flex;overflow:hidden} + +/* ─── Asset Detail ─── */ +.ad-grid{display:grid;grid-template-columns:1fr 280px;gap:16px} +.ad-dock{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:14px} +.ad-dock-box{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:6px 0;font-size:.76rem;max-height:120px;overflow-y:auto} +.ad-dock-item{display:flex;align-items:center;justify-content:space-between;padding:5px 10px;border-bottom:1px solid var(--border)} +.ad-dock-item:last-child{border-bottom:none} +.ad-undock{font-size:.68rem;color:var(--red);cursor:pointer;text-decoration:none} +.ad-undock:hover{text-decoration:underline} + +/* ─── Asset Entry Editor ─── */ +.asset-entry-hero{display:flex;justify-content:space-between;gap:18px;align-items:stretch;margin-bottom:14px;padding:18px;background:linear-gradient(135deg,var(--surface),var(--surface2));border:1px solid var(--border);border-radius:var(--radius)} +.asset-entry-title-block{display:grid;align-content:center;min-width:0} +.asset-entry-crumbs{display:flex;gap:7px;align-items:center;flex-wrap:wrap;color:var(--muted);font-family:var(--font-data);font-size:.6rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;margin-bottom:8px} +.asset-entry-crumbs span:nth-child(even){color:rgba(148,163,184,.48)} +.asset-entry-title-block h1{font-family:var(--font-data);font-size:1.35rem;line-height:1.12;font-weight:700;letter-spacing:0;overflow-wrap:anywhere} +.asset-entry-title-block p{color:var(--muted);font-size:.74rem;margin-top:7px} +.asset-entry-status-grid{display:grid;grid-template-columns:repeat(2,minmax(140px,1fr));gap:9px;min-width:430px} +.asset-entry-stat{padding:12px;background:rgba(128,128,128,.035);border:1px solid var(--border);border-radius:var(--radius)} +.asset-entry-stat span{display:block;color:var(--muted);font-family:var(--font-data);font-size:.58rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase} +.asset-entry-stat strong{display:block;margin-top:7px;font-family:var(--font-data);font-size:.78rem;line-height:1.35;overflow-wrap:anywhere} +.asset-entry-stat.active{border-color:rgba(34,197,94,.32);background:rgba(34,197,94,.08)} +.asset-entry-stat.active strong{color:var(--green)} +.asset-entry-layout{display:grid;grid-template-columns:390px minmax(0,1fr);gap:14px;align-items:start} +.asset-entry-side{display:grid;gap:14px} +.asset-entry-panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden} +.asset-entry-panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:14px 15px;border-bottom:1px solid var(--border)} +.asset-entry-panel-head h2{font-family:var(--font-data);font-size:.95rem;font-weight:700} +.asset-entry-panel-head p:not(.asset-entry-kicker){color:var(--muted);font-size:.68rem;line-height:1.45;margin-top:6px;max-width:560px} +.asset-entry-kicker{font-family:var(--font-data);font-size:.58rem;color:var(--muted);font-weight:700;letter-spacing:.12em;text-transform:uppercase;margin-bottom:5px} +.asset-entry-panel-body{display:grid;gap:13px;padding:15px} +.asset-entry-context{display:grid;gap:9px;padding:12px;background:rgba(128,128,128,.035);border:1px solid var(--border);border-radius:var(--radius)} +.asset-entry-context div{display:flex;align-items:center;justify-content:space-between;gap:12px} +.asset-entry-context span{color:var(--muted);font-family:var(--font-data);font-size:.58rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase} +.asset-entry-context strong{text-align:right;font-size:.72rem;overflow-wrap:anywhere} +.asset-entry-field{display:grid;gap:6px;min-width:0} +.asset-entry-field input,.asset-entry-field select,.asset-entry-field textarea{width:100%;min-height:38px;padding:8px 10px;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:4px;font-family:var(--font-ui);font-size:.74rem} +.asset-entry-field input[disabled]{background:var(--surface2);color:var(--muted)} +.asset-entry-field textarea{min-height:82px;resize:vertical} +.asset-entry-field input:focus,.asset-entry-field select:focus,.asset-entry-field textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)} +.asset-entry-help{color:var(--muted);font-size:.64rem;line-height:1.35} +.asset-entry-two-col{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px} +.asset-entry-type-switch{display:grid;grid-template-columns:1fr 1fr;gap:7px} +.asset-entry-type-option{display:flex;align-items:center;justify-content:center;min-height:34px;padding:0 9px;background:rgba(128,128,128,.03);color:var(--muted);border:1px solid var(--border);border-radius:4px;font-size:.68rem;font-weight:700;cursor:pointer} +.asset-entry-type-option.active{color:#fff;background:var(--accent);border-color:var(--accent)} +.asset-entry-node-panel{min-height:520px} +.asset-entry-node-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap;justify-content:flex-end} +.asset-entry-chip{display:inline-flex;align-items:center;min-height:24px;padding:0 8px;border-radius:4px;background:rgba(59,130,246,.08);border:1px solid rgba(59,130,246,.2);color:var(--accent);font-family:var(--font-data);font-size:.58rem;font-weight:700} +.asset-entry-node-summary{display:flex;align-items:center;gap:7px;flex-wrap:wrap;padding:10px 15px;background:rgba(128,128,128,.025);border-bottom:1px solid var(--border)} +.asset-entry-node-count{margin-left:auto;color:var(--cyan);font-family:var(--font-data);font-size:.68rem;font-weight:700} +.asset-entry-node-pill{display:inline-flex;align-items:center;justify-content:center;min-height:23px;padding:0 8px;border-radius:999px;border:1px solid var(--border);font-family:var(--font-data);font-size:.56rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase} +.asset-entry-node-pill.start{color:var(--green);background:rgba(34,197,94,.08);border-color:rgba(34,197,94,.24)} +.asset-entry-node-pill.mid{color:var(--cyan);background:rgba(6,182,212,.08);border-color:rgba(6,182,212,.22)} +.asset-entry-node-pill.watch{color:var(--amber);background:rgba(234,179,8,.08);border-color:rgba(234,179,8,.22)} +.asset-entry-node-pill.end{color:var(--purple);background:rgba(168,85,247,.08);border-color:rgba(168,85,247,.22)} +.asset-entry-rail{display:grid;gap:0;padding:15px} +.asset-entry-empty-nodes{display:grid;justify-items:center;gap:8px;padding:42px 18px;background:rgba(128,128,128,.03);border:1px dashed var(--border);border-radius:var(--radius);text-align:center} +.asset-entry-empty-nodes strong{font-family:var(--font-data);font-size:.82rem} +.asset-entry-empty-nodes span{color:var(--muted);font-size:.72rem} +.asset-entry-rail-node{display:grid;grid-template-columns:132px 30px minmax(0,1fr);gap:9px;min-height:84px} +.asset-entry-rail-time{color:var(--cyan);font-family:var(--font-data);font-size:.68rem;font-weight:700;line-height:1.35;text-align:right;padding-top:5px;overflow-wrap:anywhere} +.asset-entry-rail-line{position:relative;display:flex;justify-content:center} +.asset-entry-rail-line::before{content:"";position:absolute;top:0;bottom:0;width:2px;background:var(--border)} +.asset-entry-rail-line span{position:relative;z-index:1;width:14px;height:14px;margin-top:4px;border-radius:50%;background:var(--cyan);box-shadow:0 0 0 4px rgba(6,182,212,.12)} +.asset-entry-rail-node:last-child .asset-entry-rail-line::before{bottom:calc(100% - 16px)} +.asset-entry-rail-copy{padding-bottom:16px;min-width:0} +.asset-entry-node-card{display:grid;gap:9px;padding:11px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius);position:relative} +.asset-entry-node-card::before{content:"";position:absolute;left:-1px;top:10px;bottom:10px;width:3px;border-radius:3px;background:var(--cyan)} +.asset-entry-rail-node:nth-child(1) .asset-entry-node-card::before{background:var(--green)} +.asset-entry-rail-node:nth-child(3) .asset-entry-node-card::before{background:var(--amber)} +.asset-entry-rail-node:last-child .asset-entry-node-card::before{background:var(--purple)} +.asset-entry-node-card-head{display:grid;grid-template-columns:78px minmax(0,1fr) auto;gap:8px;align-items:center} +.asset-entry-time-badge{display:inline-flex;align-items:center;justify-content:center;min-height:28px;padding:0 8px;border-radius:4px;background:rgba(6,182,212,.08);border:1px solid rgba(6,182,212,.22);color:var(--cyan);font-family:var(--font-data);font-size:.62rem;font-weight:700;text-align:center} +.asset-entry-node-title{min-width:0} +.asset-entry-node-title strong{display:block;font-size:.78rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.asset-entry-node-title span{display:block;color:var(--muted);font-size:.68rem;line-height:1.35;margin-top:2px;overflow-wrap:anywhere} +.asset-entry-node-fields{display:grid;grid-template-columns:minmax(0,1fr) 170px;gap:8px} +.asset-entry-node-detail{grid-column:1 / -1} +.asset-entry-footer{position:sticky;bottom:10px;z-index:12;display:flex;align-items:center;gap:8px;margin-top:14px;padding:10px;background:rgba(19,19,24,.9);border:1px solid var(--border);border-radius:var(--radius);backdrop-filter:blur(14px)} +.asset-entry-footer-spacer{flex:1} +.field-error-shell{border-color:rgba(239,68,68,.42)!important} +.field-error-input{border-color:var(--red)!important;box-shadow:0 0 0 3px rgba(239,68,68,.1)!important} + +@media (max-width: 1180px){ + .asset-entry-hero{align-items:flex-start;flex-direction:column} + .asset-entry-status-grid{min-width:0;width:100%} + .asset-entry-layout{grid-template-columns:1fr} +} + +@media (max-width: 760px){ + .asset-entry-status-grid,.asset-entry-two-col,.asset-entry-node-fields{grid-template-columns:1fr} + .asset-entry-rail-node{grid-template-columns:1fr} + .asset-entry-rail-time{text-align:left} + .asset-entry-rail-line{display:none} + .asset-entry-node-card-head{grid-template-columns:1fr} + .asset-entry-footer{bottom:0;border-radius:var(--radius) var(--radius) 0 0} +} + +/* ─── Flash Messages ─── */ +.flash-stack{margin-bottom:16px} +.flash-message{padding:8px 14px;border-radius:var(--radius);font-size:.78rem;margin-bottom:6px} +.flash-message.success{background:rgba(34,197,94,.1);color:var(--green);border:1px solid rgba(34,197,94,.2)} +.flash-message.error{background:rgba(239,68,68,.1);color:var(--red);border:1px solid rgba(239,68,68,.2)} + +/* ─── Empty State ─── */ +.empty{text-align:center;padding:40px;color:var(--muted);font-size:.82rem} + +/* ─── Utilities ─── */ +.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0} +.fi{animation:fadeIn .25s ease-out both} +@keyframes fadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}} diff --git a/migrations/versions/2a4c9d8e7f10_add_docking_events.py b/migrations/versions/2a4c9d8e7f10_add_docking_events.py new file mode 100644 index 0000000..18d4a83 --- /dev/null +++ b/migrations/versions/2a4c9d8e7f10_add_docking_events.py @@ -0,0 +1,46 @@ +"""add docking events + +Revision ID: 2a4c9d8e7f10 +Revises: 8bbf69ea2013 +Create Date: 2026-05-31 19:30:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +revision = "2a4c9d8e7f10" +down_revision = "8bbf69ea2013" +branch_labels = None +depends_on = None + + +def upgrade(): + op.create_table( + "docking_events", + sa.Column("id", sa.Uuid(), nullable=False), + sa.Column("parent_asset_id", sa.Uuid(), nullable=False), + sa.Column("child_asset_id", sa.Uuid(), nullable=True), + sa.Column("child_label", sa.String(length=255), nullable=True), + sa.Column("docked_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("undocked_at", sa.DateTime(timezone=True), nullable=True), + sa.Column("dock_note", sa.Text(), nullable=True), + sa.Column("undock_note", sa.Text(), nullable=True), + sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), + sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), + sa.ForeignKeyConstraint(["child_asset_id"], ["assets.id"], ondelete="SET NULL"), + sa.ForeignKeyConstraint(["parent_asset_id"], ["assets.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("id"), + ) + op.create_index(op.f("ix_docking_events_child_asset_id"), "docking_events", ["child_asset_id"], unique=False) + op.create_index(op.f("ix_docking_events_docked_at"), "docking_events", ["docked_at"], unique=False) + op.create_index(op.f("ix_docking_events_parent_asset_id"), "docking_events", ["parent_asset_id"], unique=False) + op.create_index(op.f("ix_docking_events_undocked_at"), "docking_events", ["undocked_at"], unique=False) + + +def downgrade(): + op.drop_index(op.f("ix_docking_events_undocked_at"), table_name="docking_events") + op.drop_index(op.f("ix_docking_events_parent_asset_id"), table_name="docking_events") + op.drop_index(op.f("ix_docking_events_docked_at"), table_name="docking_events") + op.drop_index(op.f("ix_docking_events_child_asset_id"), table_name="docking_events") + op.drop_table("docking_events")