diff --git a/themes/Theme-Velour/README.md b/themes/Theme-Velour/README.md
new file mode 100644
index 00000000..d58c1ccb
--- /dev/null
+++ b/themes/Theme-Velour/README.md
@@ -0,0 +1,34 @@
+# Velour
+
+A velvet-lounge look for Stash v0.31+. Deep plum that reads as black until lit,
+dusty rose for actions, champagne for small flourishes, and italic Instrument
+Serif titles over Outfit.
+
+- Cards are framed plum tiles: the cover sits inset with rounded corners and the
+ text runs below it, lifting with a soft rose glow on hover.
+- Controls are 10px rounded rectangles; main actions are rose pills.
+- Covers the list toolbar, filter sidebar, selection toolbar, pagination, list,
+ wall and tagger views, scene and detail pages, stats and settings, at desktop
+ and phone widths.
+- Fonts are bundled in `assets/fonts` (SIL OFL 1.1), so the theme makes no
+ external requests.
+
+Pure CSS; no change to Stash behaviour. Enable one theme at a time.
+
+## Screenshots
+
+Taken on Stash v0.31.1 with demo content: fictional titles and names, blurred openly licensed photos (credits on the [screenshots branch](https://github.com/nexalapp/CommunityScripts/tree/screenshots)).
+
+| Scenes | Scene |
+|---|---|
+|  |  |
+
+| Performer | Studios |
+|---|---|
+|  |  |
+
+| Performers | Settings |
+|---|---|
+|  |  |
+
+
diff --git a/themes/Theme-Velour/Theme-Velour.yml b/themes/Theme-Velour/Theme-Velour.yml
new file mode 100644
index 00000000..da3760c6
--- /dev/null
+++ b/themes/Theme-Velour/Theme-Velour.yml
@@ -0,0 +1,12 @@
+name: Theme - Velour
+description: A velvet lounge. Deep plum, dusty rose actions, champagne flourishes, italic serif titles, framed cards. Self-hosted fonts; no external requests.
+version: 0.1.0
+ui:
+ css:
+ - tokens.css
+ - base.css
+ - shell.css
+ - cards.css
+ - detail.css
+ assets:
+ /: assets
diff --git a/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2 b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2
new file mode 100644
index 00000000..17653042
Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400.woff2 differ
diff --git a/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2 b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2
new file mode 100644
index 00000000..ce3c417c
Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/InstrumentSerif-400i.woff2 differ
diff --git a/themes/Theme-Velour/assets/fonts/LICENSE.md b/themes/Theme-Velour/assets/fonts/LICENSE.md
new file mode 100644
index 00000000..0414b72b
--- /dev/null
+++ b/themes/Theme-Velour/assets/fonts/LICENSE.md
@@ -0,0 +1,8 @@
+# Fonts
+
+Served from this plugin, so the theme makes no external font requests. Each font is licensed under the SIL Open Font License 1.1; the full licence and copyright notice ship beside it.
+
+- **InstrumentSerif**: Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif) — `OFL-InstrumentSerif.txt`
+- **Outfit**: Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts) — `OFL-Outfit.txt`
+
+Latin subset, woff2, as distributed by Google Fonts.
diff --git a/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt b/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt
new file mode 100644
index 00000000..669ce791
--- /dev/null
+++ b/themes/Theme-Velour/assets/fonts/OFL-InstrumentSerif.txt
@@ -0,0 +1,93 @@
+Copyright 2022 The Instrument Serif Project Authors (https://github.com/Instrument/instrument-serif)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt b/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt
new file mode 100644
index 00000000..723cd447
--- /dev/null
+++ b/themes/Theme-Velour/assets/fonts/OFL-Outfit.txt
@@ -0,0 +1,93 @@
+Copyright 2021 The Outfit Project Authors (https://github.com/Outfitio/Outfit-Fonts)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/themes/Theme-Velour/assets/fonts/Outfit-var.woff2 b/themes/Theme-Velour/assets/fonts/Outfit-var.woff2
new file mode 100644
index 00000000..48aae950
Binary files /dev/null and b/themes/Theme-Velour/assets/fonts/Outfit-var.woff2 differ
diff --git a/themes/Theme-Velour/base.css b/themes/Theme-Velour/base.css
new file mode 100644
index 00000000..c211902e
--- /dev/null
+++ b/themes/Theme-Velour/base.css
@@ -0,0 +1,404 @@
+/* Buttons, form controls, menus, modals, tabs, toggles. */
+
+/* ---------- Buttons ---------- */
+.btn {
+ font-family: var(--vel-font-body);
+ font-size: 13px;
+ font-weight: 500;
+ border-radius: var(--vel-r-ctl);
+ transition: background-color 0.15s, color 0.15s, box-shadow 0.15s, transform 0.15s var(--vel-ease);
+}
+.btn:active {
+ transform: scale(0.98);
+}
+
+/* Primary actions: rose pill (controls elsewhere are rounded rectangles) */
+.btn-primary:not(.minimal),
+.btn-success {
+ border-radius: 999px !important;
+ background-color: var(--vel-acc) !important;
+ border-color: var(--vel-acc) !important;
+ color: var(--vel-acc-ink) !important;
+ font-weight: 600;
+}
+.btn-primary:not(.minimal):hover,
+.btn-success:hover {
+ background-color: var(--vel-acc-hover) !important;
+ border-color: var(--vel-acc-hover) !important;
+}
+
+/* Secondary: neutral raised pill */
+.btn-secondary,
+.btn-info,
+.btn-light,
+.btn-outline-secondary {
+ background-color: var(--vel-bg3) !important;
+ border-color: transparent !important;
+ color: var(--vel-t1) !important;
+}
+.btn-secondary:hover,
+.btn-info:hover,
+.btn-light:hover,
+.btn-outline-secondary:hover {
+ background-color: var(--vel-bg4) !important;
+}
+.btn-secondary.active,
+.btn-secondary:not(:disabled):not(.disabled):active {
+ background-color: var(--vel-bg4) !important;
+ color: var(--vel-acc) !important;
+}
+
+/* Destructive: quiet outline until hovered */
+.btn-danger {
+ background-color: transparent !important;
+ border: 1px solid var(--vel-bg3) !important;
+ color: var(--vel-t2) !important;
+}
+.btn-danger:hover {
+ background-color: var(--vel-danger) !important;
+ border-color: var(--vel-danger) !important;
+ color: #fff !important;
+}
+
+/* Icon-only "minimal" buttons */
+.btn.minimal {
+ background-color: transparent !important;
+ border-color: transparent !important;
+ color: var(--vel-t2) !important;
+ box-shadow: none !important;
+}
+.btn.minimal:hover {
+ color: var(--vel-t1) !important;
+ background-color: var(--vel-bg2) !important;
+}
+
+.btn-link {
+ color: var(--vel-acc) !important;
+}
+
+.btn:disabled,
+.btn.disabled {
+ opacity: 0.45;
+}
+
+.btn:focus,
+.btn.focus {
+ box-shadow: none !important;
+}
+.btn:focus-visible {
+ box-shadow: var(--vel-ring) !important;
+}
+
+/* ---------- Form controls ---------- */
+.form-control,
+.custom-select,
+.input-control,
+.react-select__control,
+.date-input,
+.text-input {
+ background-color: var(--vel-bg2) !important;
+ border: 1px solid transparent !important;
+ color: var(--vel-t1) !important;
+ border-radius: 10px !important;
+ font-size: 13px;
+ box-shadow: none !important;
+}
+.form-control:focus,
+.react-select__control--is-focused {
+ border-color: var(--vel-acc) !important;
+ box-shadow: 0 0 0 3px var(--vel-acc-soft) !important;
+}
+.form-control::placeholder,
+.react-select__placeholder {
+ color: var(--vel-t3) !important;
+}
+.form-control:disabled,
+.form-control[readonly] {
+ background-color: var(--vel-bg1) !important;
+ color: var(--vel-t2) !important;
+}
+textarea.form-control {
+ border-radius: 10px !important;
+}
+
+/* Single-line inputs are pills */
+input.form-control:not([type="checkbox"]):not([type="range"]),
+select.form-control,
+.custom-select {
+ border-radius: var(--vel-r-ctl) !important;
+ padding-left: 14px;
+ padding-right: 14px;
+}
+
+.input-group > .form-control:not(:last-child) {
+ border-top-right-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+}
+.input-group > .input-group-append > .btn {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.react-select__menu,
+.react-select__menu-list {
+ background-color: var(--vel-bg2) !important;
+ border-radius: var(--vel-r-card) !important;
+ box-shadow: var(--vel-shadow-pop) !important;
+}
+.react-select__option {
+ background-color: transparent !important;
+ color: var(--vel-t2) !important;
+ border-radius: 7px;
+}
+.react-select__option--is-focused,
+.react-select__option--is-selected {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+.react-select__multi-value {
+ background-color: var(--vel-bg3) !important;
+ border-radius: 999px !important;
+}
+.react-select__multi-value__label,
+.react-select__single-value,
+.react-select__input-container {
+ color: var(--vel-t1) !important;
+}
+
+label,
+.form-label,
+.col-form-label {
+ color: var(--vel-t2);
+ font-size: 13px;
+ font-weight: 500;
+}
+
+/* Checkboxes */
+input[type="checkbox"],
+input[type="radio"] {
+ accent-color: var(--vel-acc);
+}
+
+/* Toggles (Bootstrap custom-switch) */
+.custom-switch .custom-control-label::before {
+ background-color: var(--vel-bg3) !important;
+ border-color: var(--vel-bg3) !important;
+ border-radius: 999px !important;
+}
+.custom-switch .custom-control-label::after {
+ background-color: var(--vel-t3) !important;
+}
+.custom-switch .custom-control-input:checked ~ .custom-control-label::before {
+ background-color: var(--vel-acc) !important;
+ border-color: var(--vel-acc) !important;
+}
+.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
+ background-color: var(--vel-acc-ink) !important;
+}
+.custom-control-input:focus ~ .custom-control-label::before {
+ box-shadow: 0 0 0 3px var(--vel-acc-soft) !important;
+}
+
+/* Range sliders */
+input[type="range"] {
+ accent-color: var(--vel-acc);
+}
+
+/* ---------- Menus ---------- */
+.dropdown-menu,
+.popover {
+ background-color: var(--vel-bg2) !important;
+ border: 1px solid var(--vel-line) !important;
+ border-radius: var(--vel-r-card) !important;
+ box-shadow: var(--vel-shadow-pop) !important;
+ padding: 6px !important;
+ color: var(--vel-t1);
+}
+.dropdown-item {
+ color: var(--vel-t2) !important;
+ border-radius: 7px;
+ padding: 6px 10px !important;
+ font-size: 13px;
+}
+.dropdown-item:hover,
+.dropdown-item:focus {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+.dropdown-item.active,
+.dropdown-item:active {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-acc) !important;
+}
+.dropdown-divider {
+ border-color: var(--vel-line) !important;
+}
+.popover-header {
+ background: transparent !important;
+ border-color: var(--vel-line) !important;
+ color: var(--vel-t1);
+}
+.popover-body {
+ color: var(--vel-t1) !important;
+}
+.popover .arrow::after {
+ border-top-color: var(--vel-bg2) !important;
+ border-bottom-color: var(--vel-bg2) !important;
+}
+
+.tooltip-inner {
+ background-color: var(--vel-bg3);
+ color: var(--vel-t1);
+ border-radius: 8px;
+ font-size: 12px;
+}
+
+/* ---------- Modals ---------- */
+.modal-backdrop.show {
+ opacity: 1 !important;
+ background-color: rgba(0, 0, 0, 0.45) !important;
+ backdrop-filter: blur(2px);
+}
+.modal-content {
+ background-color: var(--vel-bg1) !important;
+ border: 1px solid var(--vel-line) !important;
+ border-radius: var(--vel-r-card) !important;
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
+ color: var(--vel-t1);
+}
+.modal-header,
+.modal-footer {
+ border-color: var(--vel-line) !important;
+ background-color: transparent !important;
+}
+.modal-header,
+.modal-header .modal-title {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 22px;
+}
+.modal-body {
+ background-color: transparent !important;
+}
+.close {
+ color: var(--vel-t2) !important;
+ text-shadow: none !important;
+}
+
+/* ---------- Tabs (underline) ---------- */
+.nav-tabs {
+ border-bottom: 1px solid var(--vel-line) !important;
+ gap: 18px;
+}
+.nav-tabs .nav-link {
+ background: transparent !important;
+ border: 0 !important;
+ border-bottom: 2px solid transparent !important;
+ border-radius: 0 !important;
+ color: var(--vel-t2) !important;
+ font-size: 13px;
+ font-weight: 500;
+ padding: 6px 0 9px !important;
+}
+.nav-tabs .nav-link:hover {
+ color: var(--vel-t1) !important;
+}
+.nav-tabs .nav-link.active {
+ color: var(--vel-t1) !important;
+ border-bottom-color: var(--vel-acc) !important;
+}
+.nav-tabs .nav-link .left-spacing,
+.nav-tabs .badge,
+.nav-link .count {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+ border-radius: 999px !important;
+ font-size: 11px;
+ font-weight: 500;
+ padding: 1px 7px;
+}
+
+/* Pills (settings menu) */
+.nav-pills .nav-link {
+ color: var(--vel-t2) !important;
+ border-radius: 999px !important;
+ font-size: 13px;
+ font-weight: 500;
+ padding: 7px 12px;
+}
+.nav-pills .nav-link:hover {
+ color: var(--vel-t1) !important;
+ background-color: var(--vel-bg2) !important;
+}
+.nav-pills .nav-link.active,
+.nav-pills .show > .nav-link {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+
+/* Badges and tag chips */
+.badge {
+ border-radius: 999px;
+ font-weight: 500;
+}
+.badge-secondary,
+.tag-item,
+.badge.tag-item {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+ border-radius: 999px !important;
+ font-size: 12px;
+ font-weight: 500;
+ padding: 4px 10px;
+}
+.badge-primary,
+.badge-info {
+ background-color: var(--vel-acc-soft) !important;
+ color: var(--vel-acc) !important;
+}
+
+/* Generic panels */
+.card {
+ background-color: var(--vel-bg1);
+ border: 0;
+ border-radius: var(--vel-r-card);
+ box-shadow: none;
+ color: var(--vel-t1);
+}
+.card-header,
+.card-footer {
+ background-color: transparent;
+ border-color: var(--vel-line);
+}
+
+.table {
+ color: var(--vel-t1);
+}
+.table td,
+.table th {
+ border-color: var(--vel-line) !important;
+}
+
+/* Rating stars: off-white on, neutral off */
+.rating-stars .star-fill,
+.rating-stars .filled,
+.rating-stars button.star-fill svg {
+ color: var(--vel-rating) !important;
+}
+.rating-stars .unfilled,
+.rating-stars button:not(.star-fill) svg {
+ color: var(--vel-bg4) !important;
+}
+
+/* Toasts */
+.toast {
+ background-color: var(--vel-bg2) !important;
+ border: 1px solid var(--vel-line) !important;
+ border-radius: var(--vel-r-card) !important;
+ color: var(--vel-t1);
+}
+.toast-header {
+ background-color: transparent !important;
+ color: var(--vel-t1) !important;
+ border-color: var(--vel-line) !important;
+}
diff --git a/themes/Theme-Velour/cards.css b/themes/Theme-Velour/cards.css
new file mode 100644
index 00000000..8ef2faf8
--- /dev/null
+++ b/themes/Theme-Velour/cards.css
@@ -0,0 +1,493 @@
+/* Cards. Everything is a framed plum tile: the image inset with 10px
+ corners, text below it, a rose glow on hover.
+ Stash sets each card's width inline from the zoom slider: never override
+ width here, only aspect and contents. */
+
+.grid-card {
+ margin: 6px !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-card) !important;
+ transition: transform 0.22s var(--vel-ease), box-shadow 0.22s var(--vel-ease);
+}
+.grid-card hr {
+ display: none;
+}
+.row.justify-content-center:has(> .grid-card) {
+ padding: 8px 10px 16px;
+}
+
+/* Selection check: a ring on hover, a filled wine dot when checked */
+.grid-card .card-controls {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ z-index: 4;
+ opacity: 0;
+ transition: opacity 0.15s;
+}
+.grid-card:hover .card-controls,
+.grid-card .card-controls:has(.card-check:checked) {
+ opacity: 1;
+}
+.grid-card .card-check {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 20px !important;
+ height: 20px !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ border-radius: 50% !important;
+ border: 2px solid var(--vel-t1) !important;
+ background: rgba(22, 22, 22, 0.35) !important;
+ cursor: pointer;
+}
+.grid-card .card-check:checked {
+ border-color: var(--vel-acc) !important;
+ background: var(--vel-acc) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E") center / 12px no-repeat !important;
+}
+.grid-card:has(.card-check:checked) {
+ box-shadow: var(--vel-ring) !important;
+}
+
+/* Count buttons at the foot of a card */
+.grid-card .card-popovers {
+ justify-content: flex-start !important;
+ gap: 10px;
+ margin: 0 !important;
+ padding: 0 !important;
+ flex-wrap: nowrap;
+}
+.grid-card .card-popovers .btn {
+ padding: 0 !important;
+ height: auto;
+ font-size: 11px !important;
+ font-weight: 500;
+ color: var(--vel-t2) !important;
+ background: transparent !important;
+}
+.grid-card .card-popovers .btn:hover {
+ color: var(--vel-t1) !important;
+}
+.grid-card .card-popovers svg {
+ width: 11px;
+ margin-right: 4px;
+ opacity: 0.8;
+}
+
+/* ================= Framed media cards: scenes, performers =================
+ A plum tile with 5px of padding; the image sits inset with 10px corners
+ and the text runs below it. */
+.scene-card.grid-card,
+.performer-card.grid-card {
+ position: relative;
+ overflow: hidden;
+ padding: 5px !important;
+ background-color: var(--vel-bg1) !important;
+}
+.scene-card.grid-card:hover,
+.performer-card.grid-card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--vel-glow);
+}
+
+.scene-card .thumbnail-section,
+.performer-card .thumbnail-section {
+ position: relative !important;
+ margin: 0 !important;
+ border-radius: 10px;
+ overflow: hidden;
+ background: var(--vel-media);
+ max-height: none !important;
+}
+.scene-card .thumbnail-section {
+ aspect-ratio: 16 / 9;
+ height: auto !important;
+}
+.performer-card .thumbnail-section {
+ aspect-ratio: 2 / 3;
+ height: auto !important;
+}
+.scene-card .scene-card-link,
+.scene-card .scene-card-preview,
+.performer-card .thumbnail-section > a:first-child {
+ display: block;
+ height: 100% !important;
+ max-height: none !important;
+ aspect-ratio: auto !important;
+}
+.scene-card .scene-card-preview-image,
+.scene-card .scene-card-preview-video,
+.performer-card .performer-card-image {
+ width: 100% !important;
+ height: 100% !important;
+ max-height: none !important;
+ min-width: 0 !important;
+ aspect-ratio: auto !important;
+ object-fit: cover;
+ object-position: center top;
+ border-radius: 0 !important;
+}
+
+/* Text below the image */
+.scene-card .card-section,
+.performer-card .card-section {
+ padding: 10px 9px 4px !important;
+ margin: 0 !important;
+ background: transparent !important;
+ display: grid;
+ gap: 3px;
+}
+.scene-card .card-popovers,
+.performer-card .card-popovers {
+ padding: 3px 9px 6px !important;
+}
+.scene-card .card-popovers .btn,
+.performer-card .card-popovers .btn {
+ color: var(--vel-t3) !important;
+}
+
+.scene-card .card-section-title,
+.performer-card .card-section-title {
+ font-family: var(--vel-font-title) !important;
+ font-style: var(--vel-title-style);
+ font-size: var(--vel-title-card) !important;
+ line-height: var(--vel-title-lh) !important;
+ font-weight: 400 !important;
+ color: var(--vel-t1) !important;
+ text-shadow: none !important;
+ margin: 0 !important;
+}
+.scene-card .card-section-title .TruncatedText {
+ -webkit-line-clamp: 2 !important;
+}
+.scene-card .scene-card__details {
+ display: grid;
+ gap: 3px;
+ margin: 0 !important;
+}
+.scene-card .scene-card__date {
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--vel-t3) !important;
+}
+.scene-card .scene-card__description {
+ font-size: 12px !important;
+ line-height: 1.4 !important;
+ color: var(--vel-t2) !important;
+ -webkit-line-clamp: 2 !important;
+ margin: 0 !important;
+}
+.scene-card .extra-scene-info {
+ display: none !important;
+}
+
+/* "1080P · 21:04" in champagne, bottom right of the image */
+.scene-card .scene-specs-overlay {
+ position: absolute !important;
+ top: auto !important;
+ right: 8px !important;
+ bottom: 8px !important;
+ left: auto !important;
+ z-index: 3;
+ padding: 3px 7px;
+ border-radius: var(--vel-r-chip);
+ background: rgba(0, 0, 0, 0.55);
+ color: var(--vel-acc2) !important;
+ font-size: 10px !important;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ text-shadow: none !important;
+ display: flex;
+}
+.scene-card .scene-specs-overlay > span {
+ font-size: 10px !important;
+ font-weight: 600 !important;
+ margin: 0 !important;
+ padding: 0 !important;
+ color: inherit !important;
+ text-shadow: none !important;
+}
+.scene-card .scene-specs-overlay .overlay-resolution + .overlay-duration::before {
+ content: "\00a0·\00a0";
+}
+
+/* Studio logo, top left of the image */
+.scene-card .studio-overlay {
+ position: absolute !important;
+ top: 8px !important;
+ left: 8px !important;
+ right: auto !important;
+ z-index: 3;
+ max-width: 45%;
+ padding: 3px 7px;
+ border-radius: var(--vel-r-chip);
+ background: rgba(0, 0, 0, 0.45);
+ opacity: 0.9;
+ height: auto !important;
+ display: flex;
+ align-items: center;
+}
+/* Stock gives the logo link a fixed height; let the chip wrap the logo */
+.scene-card .studio-overlay a {
+ display: block;
+ height: auto !important;
+ line-height: 0;
+}
+.scene-card .studio-overlay .image-thumbnail {
+ display: block;
+ height: 18px !important;
+ max-height: 18px !important;
+ max-width: 100%;
+ width: auto;
+ object-fit: contain;
+}
+
+/* The selection check sits top right on these cards */
+.scene-card .card-controls,
+.performer-card .card-controls {
+ left: auto !important;
+ right: 13px;
+ top: 13px;
+}
+
+.scene-card .progress-bar {
+ z-index: 3;
+ background-color: var(--vel-acc) !important;
+}
+
+/* Rating as a small chip */
+.grid-card .rating-banner {
+ transform: none !important;
+ left: 13px !important;
+ right: auto !important;
+ top: auto !important;
+ bottom: auto !important;
+ width: auto !important;
+ padding: 2px 7px !important;
+ border-radius: var(--vel-r-chip) !important;
+ background: rgba(0, 0, 0, 0.55) !important;
+ color: var(--vel-acc) !important;
+ font-size: 10px !important;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ z-index: 3;
+ box-shadow: none !important;
+}
+
+/* ---- Performer specifics ---- */
+.performer-card .card-section-title {
+ display: flex !important;
+ align-items: baseline;
+ gap: 8px;
+}
+.performer-card .card-section-title .gender-icon,
+.performer-card .card-section-title svg {
+ color: var(--vel-t3) !important;
+ width: 12px;
+ flex: none;
+}
+.performer-card .card-section-title span,
+.performer-card .performer-name {
+ font-size: inherit !important;
+}
+.performer-card .performer-card__age {
+ font-size: 11px !important;
+ font-weight: 500;
+ color: var(--vel-t3) !important;
+}
+.performer-card .performer-card__country-flag {
+ display: none !important;
+}
+.performer-card .thumbnail-section > a:has(.performer-card__country-string) {
+ position: absolute;
+ right: 8px;
+ bottom: 8px;
+ height: auto !important;
+ z-index: 3;
+}
+.performer-card .performer-card__country-string {
+ display: inline-block !important;
+ padding: 3px 7px;
+ border-radius: var(--vel-r-chip);
+ background: rgba(0, 0, 0, 0.55);
+ color: var(--vel-t1);
+ font-size: 10px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+}
+/* Favourite heart: top-right, mirroring the selection check on the left, at
+ the same 20px. No black halo or pill; off-white until it's a favourite. */
+.grid-card button.btn.favorite-button {
+ position: absolute !important;
+ z-index: 4;
+ top: 10px !important;
+ right: 10px !important;
+ left: auto !important;
+ width: 20px;
+ height: 20px;
+ padding: 0 !important;
+ margin: 0 !important;
+ display: grid;
+ place-items: center;
+ background: none !important;
+ border: 0 !important;
+ box-shadow: none !important;
+ filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.45)) !important;
+ color: var(--vel-acc) !important;
+}
+.grid-card button.btn.favorite-button svg.fa-icon,
+.grid-card button.btn.favorite-button svg {
+ width: 18px !important;
+ height: 18px !important;
+ font-size: 18px !important;
+ margin: 0 !important;
+}
+.grid-card button.btn.favorite-button.not-favorite {
+ color: rgba(243, 240, 238, 0.85) !important;
+}
+.grid-card button.btn.favorite-button:hover {
+ background: none !important;
+ transform: scale(1.1);
+}
+
+/* ================= Framed tiles: studios, tags, galleries, groups ================= */
+.studio-card.grid-card,
+.tag-card.grid-card,
+.gallery-card.grid-card,
+.group-card.grid-card,
+.image-card.grid-card {
+ background-color: var(--vel-bg1) !important;
+ padding: 5px !important;
+ overflow: hidden;
+}
+.studio-card.grid-card:hover,
+.tag-card.grid-card:hover,
+.gallery-card.grid-card:hover,
+.group-card.grid-card:hover,
+.image-card.grid-card:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--vel-glow);
+}
+.studio-card .thumbnail-section,
+.tag-card .thumbnail-section,
+.gallery-card .thumbnail-section,
+.group-card .thumbnail-section,
+.image-card .thumbnail-section {
+ border-radius: 10px;
+ overflow: hidden;
+ background-color: var(--vel-bg2);
+ position: relative;
+}
+/* Fixed 16:9 logo box. The logo is positioned inside it, so a tall logo
+ can't stretch the box and every tile in a grid comes out the same size. */
+.studio-card .studio-card-header,
+.tag-card .tag-card-header {
+ display: block !important;
+ position: relative;
+ aspect-ratio: 16 / 9;
+ width: 100%;
+ height: auto !important;
+ min-height: 0 !important;
+ max-height: none !important;
+ overflow: hidden;
+ background-color: var(--vel-bg2);
+}
+.studio-card .studio-card-image,
+.tag-card .tag-card-image {
+ position: absolute;
+ inset: 0;
+ width: 100% !important;
+ height: 100% !important;
+ max-width: none !important;
+ max-height: none !important;
+ min-height: 0 !important;
+ object-fit: contain;
+ padding: 10% 12%;
+ aspect-ratio: auto !important;
+}
+.gallery-card .gallery-card-image,
+.group-card .group-card-image,
+.image-card .image-card-preview-image {
+ border-radius: 0;
+ object-fit: cover;
+}
+.studio-card .card-section,
+.tag-card .card-section,
+.gallery-card .card-section,
+.group-card .card-section,
+.image-card .card-section {
+ padding: 10px 9px 4px !important;
+ background: transparent !important;
+}
+.studio-card .card-section-title,
+.tag-card .card-section-title,
+.gallery-card .card-section-title,
+.group-card .card-section-title,
+.image-card .card-section-title {
+ font-size: var(--vel-title-card) !important;
+ line-height: var(--vel-title-lh) !important;
+ color: var(--vel-t1) !important;
+ margin-bottom: 3px !important;
+ text-align: left !important;
+}
+.studio-card__details,
+.studio-card__details a,
+.tag-card .tag-description,
+.gallery-card__details,
+.group-card__details {
+ font-size: 11px !important;
+ color: var(--vel-t3) !important;
+ font-weight: 500;
+}
+.studio-card .card-popovers,
+.tag-card .card-popovers,
+.gallery-card .card-popovers,
+.group-card .card-popovers,
+.image-card .card-popovers {
+ padding: 2px 9px 6px !important;
+}
+.studio-card .card-popovers .btn,
+.tag-card .card-popovers .btn,
+.gallery-card .card-popovers .btn,
+.group-card .card-popovers .btn {
+ color: var(--vel-t3) !important;
+}
+
+/* Home-page carousels ("Recently added ...") size each card from its
+ contents, so a wide studio logo made a wide card. Pin them like stock. */
+.recommendation-row .grid-card {
+ width: 320px !important;
+ max-width: 320px;
+}
+.recommendation-row .studio-card .studio-card-image,
+.recommendation-row .tag-card .tag-card-image {
+ max-width: 100% !important;
+}
+
+/* Reserve the "Part of …" line so studio tiles stay the same height with or
+ without a parent studio. */
+.studio-card .studio-card__details {
+ min-height: 1.5em;
+}
+
+/* Performer cards have no studio chip, so the check goes back top-left and
+ the favourite heart takes the top-right corner. */
+.performer-card .card-controls {
+ left: 13px !important;
+ right: auto !important;
+}
+.performer-card button.btn.favorite-button {
+ top: 8px !important; /* + the 5px tile padding = 13px, matching the check */
+ right: 8px !important;
+}
+
+/* Framed tiles place the heart inside the 5px-inset image box; offset it so
+ it sits as far from the corner as the check does. */
+.studio-card button.btn.favorite-button,
+.tag-card button.btn.favorite-button,
+.gallery-card button.btn.favorite-button,
+.group-card button.btn.favorite-button {
+ top: 5px !important;
+ right: 5px !important;
+}
diff --git a/themes/Theme-Velour/detail.css b/themes/Theme-Velour/detail.css
new file mode 100644
index 00000000..7a04e50b
--- /dev/null
+++ b/themes/Theme-Velour/detail.css
@@ -0,0 +1,480 @@
+/* Scene page, entity detail headers, list/wall/tagger views, stats,
+ settings, empty states. */
+
+/* Bootstrap utility Stash uses for menus and panels */
+.bg-secondary {
+ background-color: var(--vel-bg2) !important;
+}
+.dropdown-menu .bg-secondary.dropdown-item {
+ background-color: transparent !important;
+}
+.dropdown-menu .bg-secondary.dropdown-item:hover,
+.dropdown-menu .bg-secondary.dropdown-item:focus {
+ background-color: var(--vel-bg3) !important;
+}
+.text-white {
+ color: var(--vel-t1) !important;
+}
+
+/* ================= Scene page ================= */
+.scene-tabs {
+ background-color: var(--vel-bg1);
+ border-right: 1px solid var(--vel-line);
+ padding: 0 16px 16px !important;
+}
+.scene-tabs .scene-header-container .studio-logo {
+ margin: 0 -16px 18px !important;
+ padding: 16px;
+ min-height: 0;
+}
+.scene-tabs .scene-header-container img.studio-logo {
+ max-height: 100px;
+ object-fit: contain;
+}
+.scene-tabs .scene-header {
+ font-family: var(--vel-font-title) !important;
+ font-style: var(--vel-title-style);
+ font-size: var(--vel-title-page) !important;
+ line-height: 1.1 !important;
+ font-weight: 400;
+ color: var(--vel-t1);
+ margin-bottom: 6px;
+}
+.scene-subheader {
+ font-size: 13px;
+ color: var(--vel-t2);
+}
+.scene-subheader .resolution {
+ color: var(--vel-t1);
+ font-weight: 600;
+}
+.scene-subheader .divider {
+ opacity: 0;
+ margin: 0 2px;
+}
+.scene-subheader .frame-rate::after {
+ content: " ·";
+}
+.scene-toolbar {
+ color: var(--vel-t2);
+ margin-top: 4px;
+}
+.scene-tabs .nav-tabs {
+ margin-top: 10px;
+}
+.scene-details h6,
+.scene-file-info dt,
+.scene-file-info dd,
+.scene-details .detail-item {
+ font-family: var(--vel-font-body);
+ font-size: 13px !important;
+ font-weight: 400;
+ line-height: 1.6;
+ color: var(--vel-t2);
+}
+.scene-details a,
+.scene-file-info a {
+ color: var(--vel-acc) !important;
+}
+.scene-description,
+.scene-details .TruncatedText {
+ font-size: 13px;
+ line-height: 1.6;
+ color: var(--vel-t2);
+}
+.scene-file-info dt {
+ color: var(--vel-t1);
+ font-weight: 500;
+}
+
+/* The collapse handle between the panel and the player */
+.scene-divider .btn {
+ background-color: transparent !important;
+ border: 0 !important;
+ color: var(--vel-t3) !important;
+ width: 14px;
+ padding: 0 !important;
+ border-radius: 0 !important;
+}
+.scene-divider .btn:hover {
+ background-color: var(--vel-bg2) !important;
+ color: var(--vel-t1) !important;
+}
+
+/* Player */
+.scene-player-container {
+ background-color: var(--vel-bg0);
+}
+.video-js .vjs-play-progress,
+.video-js .vjs-volume-level {
+ background-color: var(--vel-acc) !important;
+}
+.video-js .vjs-load-progress {
+ background: rgba(255, 255, 255, 0.25) !important;
+}
+.video-js .vjs-progress-holder {
+ background: rgba(255, 255, 255, 0.18) !important;
+ border-radius: 2px;
+}
+.video-js .vjs-control-bar {
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.7)) !important;
+}
+.video-js .vjs-big-play-button {
+ width: 72px !important;
+ height: 72px !important;
+ line-height: 72px !important;
+ margin-left: -36px !important;
+ margin-top: -36px !important;
+ border: 0 !important;
+ border-radius: 50% !important;
+ background-color: rgba(255, 255, 255, 0.14) !important;
+ backdrop-filter: blur(6px);
+}
+
+/* Scrubber strip under the player */
+.scrubber-wrapper {
+ gap: 8px;
+ padding: 10px 16px 12px;
+ margin: 0 !important;
+}
+.scrubber-wrapper .scrubber-button {
+ width: 22px !important;
+ min-width: 22px;
+ padding: 0 !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-chip) !important;
+ background-color: var(--vel-bg2) !important;
+ color: var(--vel-acc) !important;
+ font-weight: 400;
+}
+.scrubber-wrapper .scrubber-button:hover {
+ background-color: var(--vel-bg3) !important;
+}
+.scrubber-content {
+ border-radius: var(--vel-r-chip);
+ background-color: var(--vel-bg1);
+ margin: 0 !important;
+}
+.scrubber-tags-background {
+ background-color: var(--vel-bg3) !important;
+}
+#scrubber-current-position {
+ background-color: var(--vel-t1) !important;
+}
+.scrubber-tag {
+ background-color: var(--vel-acc) !important;
+ color: var(--vel-acc-ink) !important;
+ border-radius: 4px;
+}
+
+/* Edit panels: Save stands out, the rest stay neutral */
+.details-edit .btn-primary:not(.edit):not(.save),
+.scraper-menu .btn-primary,
+.search-item .btn-primary:not(.minimal):not(.input-group-append .btn),
+.setting-section .btn-primary:not(.minimal),
+#settings-menu-container .btn-primary {
+ border-radius: var(--vel-r-ctl) !important;
+ background-color: var(--vel-bg3) !important;
+ border-color: transparent !important;
+ color: var(--vel-t1) !important;
+ font-weight: 500;
+}
+#scene-edit-details .edit-buttons-container,
+.edit-buttons-container {
+ background-color: var(--vel-bg1) !important;
+}
+.form-group .col-form-label,
+.form-group label {
+ color: var(--vel-t2);
+}
+
+/* ================= Detail headers: performer, studio, tag, group ================= */
+.detail-header,
+.detail-header.full-width {
+ background-color: var(--vel-bg1) !important;
+ border-bottom: 1px solid var(--vel-line);
+ color: var(--vel-t1);
+}
+.detail-header .background-image-container {
+ opacity: 0.18;
+}
+.detail-header-image {
+ position: relative;
+}
+.detail-header-image .btn-link {
+ padding: 0 !important;
+}
+.detail-header-image img.performer,
+.detail-header-image img {
+ border-radius: 0 !important;
+ box-shadow: none !important;
+}
+.detail-header h2,
+.detail-header .performer-name,
+.detail-header .studio-name,
+.detail-header .tag-name {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 40px;
+ line-height: 1;
+ font-weight: 400;
+ color: var(--vel-t1);
+}
+.detail-header .alias-head {
+ font-size: 13px;
+ color: var(--vel-t2);
+}
+.detail-header .detail-item {
+ font-size: 13px;
+ line-height: 1.75;
+}
+.detail-header .detail-item-title {
+ color: var(--vel-t3);
+ font-weight: 500;
+ width: 130px;
+ display: inline-block;
+}
+.detail-header .detail-item-value {
+ color: var(--vel-t1);
+}
+.detail-header .details-edit {
+ gap: 8px;
+ margin-top: 10px;
+}
+.detail-header .name-icons .favorite-button:not(.not-favorite) {
+ color: var(--vel-acc) !important;
+}
+.detail-header .name-icons .link,
+.detail-header a {
+ color: var(--vel-acc);
+}
+
+/* StashDB / stash-box ID chips */
+.stash-id-pill,
+.stash-id-pill span,
+.stash-id-pill a {
+ border-radius: 999px !important;
+}
+.stash-id-pill span:first-child {
+ background-color: var(--vel-acc-soft) !important;
+ color: var(--vel-acc) !important;
+}
+.stash-id-pill span:last-child,
+.stash-id-pill a {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+
+/* Tabs under a detail header, centred */
+.detail-body .nav-tabs,
+.performer-tabs .nav-tabs,
+.studio-tabs .nav-tabs,
+.tag-tabs .nav-tabs {
+ justify-content: center;
+ gap: 22px;
+}
+
+/* Sticky header shown when scrolled */
+.sticky.detail-header {
+ background-color: rgba(30, 30, 30, 0.92) !important;
+ backdrop-filter: blur(10px);
+}
+.sticky.detail-header .sticky.detail-header-group a,
+.sticky.detail-header .sticky.detail-header-group span {
+ color: var(--vel-t1) !important;
+}
+
+/* ================= List (table) view ================= */
+.table-list table {
+ color: var(--vel-t1);
+}
+.table-list table thead th {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--vel-t2) !important;
+ border-bottom: 1px solid var(--vel-line) !important;
+ border-top: 0 !important;
+}
+.table-list table tbody tr {
+ border-bottom: 1px solid var(--vel-line);
+}
+.table-list table tbody tr:hover {
+ background-color: var(--vel-bg1);
+}
+.table-list table td {
+ vertical-align: middle;
+ border: 0 !important;
+ font-size: 13px;
+}
+.table-list table td img {
+ border-radius: 8px;
+}
+.table-list .title-link,
+.table-list td.title-data a,
+.table-list td a.performer-name {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 16px;
+ color: var(--vel-t1) !important;
+}
+.table-list td a {
+ color: var(--vel-t1);
+}
+
+/* ================= Wall view ================= */
+.wall-item,
+.wall-item-container {
+ border-radius: var(--vel-r-chip) !important;
+ overflow: hidden;
+}
+.wall-item-text,
+.wall-item .wall-item-text {
+ background: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8)) !important;
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 18px;
+ color: var(--vel-t1) !important;
+ text-shadow: none !important;
+}
+
+/* ================= Tagger ================= */
+.tagger-container .search-item,
+.tagger-container .tagger-item,
+.search-item {
+ background-color: var(--vel-bg1) !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-card) !important;
+ padding: 14px !important;
+ margin-bottom: 12px;
+}
+.tagger-container .search-item .scene-card-link img,
+.search-item img {
+ border-radius: 10px;
+}
+.tagger-container .input-group-prepend .input-group-text,
+.input-group-text {
+ background-color: var(--vel-bg3) !important;
+ border: 0 !important;
+ color: var(--vel-t2) !important;
+ font-size: 12px;
+ font-weight: 500;
+}
+.tagger-container .input-group .form-control {
+ border-radius: 0 !important;
+}
+.tagger-container .input-group > :first-child,
+.tagger-container .input-group > :first-child .input-group-text {
+ border-radius: var(--vel-r-ctl) 0 0 var(--vel-r-ctl) !important;
+}
+.tagger-container .input-group .input-group-append .btn {
+ border-radius: 0 var(--vel-r-ctl) var(--vel-r-ctl) 0 !important;
+}
+
+/* ================= Stats ================= */
+#stats-page .stats-element,
+.stats .stats-element {
+ text-align: center;
+}
+#stats-page .stats-element .title,
+.stats .title {
+ font-family: var(--vel-font-title) !important;
+ font-style: var(--vel-title-style);
+ font-size: 52px !important;
+ line-height: 1;
+ font-weight: 400 !important;
+ color: var(--vel-t1) !important;
+}
+#stats-page .stats-element .heading,
+.stats .heading {
+ margin-top: 12px;
+ font-size: 11px !important;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--vel-t3) !important;
+ font-weight: 500;
+}
+
+/* ================= Settings ================= */
+#settings-menu-container .nav-pills,
+.settings-menu-container .nav-pills {
+ gap: 2px;
+}
+#settings-container h1,
+#settings-container h2,
+#settings-container h3,
+.setting-section h1 {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 28px;
+ font-weight: 400;
+ color: var(--vel-t1);
+}
+.setting-section .card,
+#settings-container .card,
+#settings-container .setting-group {
+ background-color: var(--vel-bg1) !important;
+ border-radius: var(--vel-r-card) !important;
+ border: 0 !important;
+}
+.setting-section .setting,
+#settings-container .setting {
+ border-color: var(--vel-line) !important;
+}
+.setting-section .setting h3,
+.setting-section .setting-group h3,
+#settings-container .setting h3,
+#settings-container .setting-group h3,
+#settings-container .setting-section h3,
+.setting-section h3 {
+ font-family: var(--vel-font-body);
+ font-style: normal !important;
+ font-size: 15px;
+ font-weight: 600;
+}
+.setting .sub-heading,
+.setting-section .sub-heading,
+#settings-container .sub-heading {
+ font-size: 12px;
+ color: var(--vel-t2) !important;
+}
+.job-table.card {
+ background-color: var(--vel-bg1) !important;
+ border-radius: var(--vel-r-card) !important;
+ color: var(--vel-t2);
+}
+#tasks-panel .tasks-panel-queue h1 {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+}
+.logs .log-entry,
+.LogEntry,
+#log-entries {
+ font-size: 12px;
+}
+
+/* ================= Empty list ================= */
+.item-list-container .sidebar-pane-content:not(:has(.grid-card, .table-list, .scene-wall, .wall, .tagger-container, .card, [class*="wall"])) > .pagination-index-container::after {
+ content: "Nothing here yet";
+ display: block;
+ margin: 18vh auto 0;
+ text-align: center;
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 26px;
+ color: var(--vel-t2);
+}
+
+/* Big play button: Stash draws the icon at 100px, larger than the 72px circle,
+ so it overflowed off-centre. Size the glyph to the circle and nudge it right
+ so the triangle looks optically centred. */
+.video-js .vjs-big-play-button {
+ font-size: 36px !important;
+ border: 0 !important;
+}
+.video-js .vjs-big-play-button .vjs-icon-placeholder::before {
+ font-size: 36px !important;
+ line-height: 72px !important;
+ width: 100% !important;
+ height: 100% !important;
+ text-indent: 3px;
+}
diff --git a/themes/Theme-Velour/shell.css b/themes/Theme-Velour/shell.css
new file mode 100644
index 00000000..4ccd2dc9
--- /dev/null
+++ b/themes/Theme-Velour/shell.css
@@ -0,0 +1,422 @@
+/* Top bar, list toolbar, pagination, result count, filter sidebar. */
+
+/* ---------- Top bar ---------- */
+.top-nav,
+.top-nav.bg-dark,
+.top-nav .bg-dark {
+ background-color: var(--vel-bg1) !important;
+}
+.top-nav {
+ border-bottom: 1px solid var(--vel-line);
+ box-shadow: none !important;
+ padding-left: 16px !important;
+ padding-right: 16px !important;
+}
+
+.top-nav .navbar-brand .brand-link {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-size: 22px !important;
+ line-height: 1;
+ color: var(--vel-t1) !important;
+ background: transparent !important;
+ padding: 0 18px 0 0 !important;
+ letter-spacing: 0;
+}
+.top-nav .navbar-brand .brand-link:hover {
+ background: transparent !important;
+}
+
+@media (min-width: 1200px) {
+ .top-nav .navbar-nav .nav-link {
+ padding: 0 1px !important;
+ }
+ .top-nav .navbar-nav .nav-link > a.btn {
+ padding: 6px 12px !important;
+ border-radius: 999px !important;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--vel-t2) !important;
+ }
+ .top-nav .navbar-nav .nav-link > a.btn:hover {
+ background-color: var(--vel-bg2) !important;
+ color: var(--vel-t1) !important;
+ }
+ .top-nav .navbar-nav .nav-link > a.btn.active {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+ }
+ /* Words, not icons, in the desktop bar */
+ .top-nav .navbar-nav .nav-link > a.btn .nav-menu-icon {
+ display: none !important;
+ }
+}
+
+.top-nav .nav-utility .btn,
+.top-nav .navbar-buttons .btn.minimal,
+.top-nav .logout-button {
+ color: var(--vel-t2) !important;
+ border-radius: 999px !important;
+}
+.top-nav .navbar-buttons a[href$="/new"] .btn {
+ height: 32px;
+ padding: 0 14px;
+ border-radius: 999px !important;
+}
+
+/* Theme Switch's own menu */
+.theme-plugin-menu .card,
+.theme-plugin-menu .card-header,
+.theme-plugin-menu .modal-header {
+ background-color: transparent !important;
+ color: var(--vel-t1) !important;
+ border-color: var(--vel-line) !important;
+ font-family: var(--vel-font-body);
+ font-size: 13px;
+}
+
+/* ---------- Page container ---------- */
+.main.container-fluid {
+ background: transparent;
+}
+
+/* ---------- List toolbar ---------- */
+.filtered-list-toolbar {
+ background-color: transparent !important;
+ padding-top: 12px !important;
+ padding-bottom: 0 !important;
+ gap: 8px !important;
+}
+.filtered-list-toolbar > * {
+ margin: 0 !important;
+}
+
+/* Grouped controls become one pill with segments inside */
+.filtered-list-toolbar .btn-group,
+.filtered-list-toolbar .sort-by-select {
+ background-color: var(--vel-bg2);
+ border-radius: var(--vel-r-ctl);
+ padding: 3px;
+ gap: 2px;
+ height: var(--vel-ctl-h);
+ align-items: center;
+ border: 0 !important;
+}
+.filtered-list-toolbar .btn-group .btn-group,
+.filtered-list-toolbar .list-operations .btn-group .btn-group {
+ padding: 0;
+ background: transparent;
+ height: auto;
+}
+.filtered-list-toolbar .btn-group > .btn,
+.filtered-list-toolbar .btn-group > .btn-group > .btn,
+.filtered-list-toolbar .btn-group > .dropdown > .btn,
+.filtered-list-toolbar .sort-by-select .btn {
+ background-color: transparent !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-in) !important;
+ color: var(--vel-t2) !important;
+ height: 28px;
+ min-width: 30px;
+ padding: 0 9px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 !important;
+}
+.filtered-list-toolbar .btn-group .btn:hover,
+.filtered-list-toolbar .sort-by-select .btn:hover {
+ color: var(--vel-t1) !important;
+ background-color: var(--vel-bg3) !important;
+}
+.filtered-list-toolbar .btn-group .btn.active {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-acc) !important;
+}
+
+/* Sort: label, hairline, direction */
+.filtered-list-toolbar .sort-by-select .input-group-prepend .btn {
+ color: var(--vel-t1) !important;
+ font-weight: 500;
+ padding: 0 12px;
+}
+.filtered-list-toolbar .sort-by-select .input-group-prepend {
+ margin: 0;
+ border-right: 1px solid var(--vel-line);
+}
+.filtered-list-toolbar .sort-by-select .dropdown-toggle::after {
+ display: none;
+}
+
+/* Search */
+.filtered-list-toolbar .search-term-input .form-control,
+.filtered-list-toolbar .search-term-input input {
+ height: var(--vel-ctl-h);
+ width: 220px;
+ padding-left: 34px !important;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236f6a66' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cpath d='M20 20l-3.5-3.5'/%3E%3C/svg%3E") !important;
+ background-repeat: no-repeat !important;
+ background-position: 13px center !important;
+}
+
+/* Page size */
+.filtered-list-toolbar .page-size-selector select {
+ height: var(--vel-ctl-h);
+ font-weight: 500;
+ border: 0 !important;
+}
+
+/* Zoom slider: hairline track, light knob */
+.filtered-list-toolbar .zoom-slider-container {
+ margin-left: 4px !important;
+}
+input[type="range"].zoom-slider {
+ -webkit-appearance: none;
+ appearance: none;
+ background: transparent !important;
+ width: 70px;
+ max-width: 70px;
+}
+input[type="range"].zoom-slider::-webkit-slider-runnable-track {
+ height: 3px;
+ border-radius: 2px;
+ background: var(--vel-bg3) !important;
+}
+input[type="range"].zoom-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ width: 13px;
+ height: 13px;
+ margin-top: -5px;
+ border-radius: 50%;
+ background: var(--vel-t1) !important;
+ border: 0;
+}
+input[type="range"].zoom-slider::-moz-range-track {
+ height: 3px;
+ border-radius: 2px;
+ background: var(--vel-bg3) !important;
+}
+input[type="range"].zoom-slider::-moz-range-progress {
+ height: 3px;
+ border-radius: 2px;
+ background: var(--vel-acc);
+}
+input[type="range"].zoom-slider::-moz-range-thumb {
+ width: 13px;
+ height: 13px;
+ border-radius: 50%;
+ background: var(--vel-t1) !important;
+ border: 0;
+}
+
+/* Filter-sidebar toggle */
+.sidebar-toggle-button-container .sidebar-toggle-button {
+ width: var(--vel-ctl-h);
+ height: var(--vel-ctl-h);
+ padding: 0 !important;
+ margin-left: 0 !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-ctl) !important;
+ background-color: var(--vel-bg2) !important;
+ color: var(--vel-t2) !important;
+ opacity: 1 !important;
+ top: calc(48.75px + 12px) !important;
+ display: grid;
+ place-items: center;
+}
+.sidebar-toggle-button-container .sidebar-toggle-button:hover {
+ color: var(--vel-t1) !important;
+ background-color: var(--vel-bg3) !important;
+}
+
+/* Selection toolbar: sticky over the grid, so it needs a solid backing */
+.item-list-container .filtered-list-toolbar.has-selection {
+ background-color: var(--vel-bg0) !important;
+ border: 1px solid var(--vel-line);
+ border-radius: var(--vel-r-card);
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
+ padding: 6px 8px !important;
+}
+.filtered-list-toolbar.has-selection .selected-items-info,
+.filtered-list-toolbar.has-selection .selected-items-info span {
+ color: var(--vel-acc) !important;
+ font-weight: 600;
+}
+.filtered-list-toolbar .btn.btn-danger,
+.filtered-list-toolbar .btn-group > .btn.btn-danger {
+ background-color: var(--vel-danger) !important;
+ color: #fff !important;
+}
+
+/* ---------- Pagination ---------- */
+.pagination-index-container {
+ margin-top: 10px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+.pagination.btn-group {
+ width: auto !important;
+ flex: none !important;
+ background-color: var(--vel-bg2);
+ border-radius: var(--vel-r-ctl);
+ padding: 3px;
+ gap: 2px;
+ margin-bottom: 0 !important;
+ box-shadow: none !important;
+}
+.pagination .btn,
+.pagination .page-count-container .btn {
+ background-color: transparent !important;
+ border: 0 !important;
+ border-radius: var(--vel-r-in) !important;
+ color: var(--vel-t2) !important;
+ height: 26px;
+ min-width: 28px;
+ padding: 0 8px !important;
+ font-size: 12px;
+ font-weight: 500;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+.pagination .btn:hover:not(:disabled) {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+.pagination .btn:disabled {
+ color: var(--vel-t3) !important;
+ opacity: 1;
+}
+.pagination .page-count {
+ color: var(--vel-t1) !important;
+ padding: 0 4px 0 10px !important;
+}
+.pagination .btn.active,
+.pagination .btn-secondary.active {
+ background-color: var(--vel-bg3) !important;
+ color: var(--vel-t1) !important;
+}
+.pagination-footer .pagination.btn-group {
+ box-shadow: var(--vel-shadow-pop) !important;
+ border: 1px solid var(--vel-line);
+}
+
+/* "1–40 of 6,715 · 3M 1W 3D · 7.9 TiB" */
+.paginationIndex {
+ display: block !important;
+ text-align: center;
+ margin: 8px 0 0 !important;
+ font-size: 11px !important;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--vel-t3) !important;
+}
+.paginationIndex br {
+ display: none;
+}
+.paginationIndex .scenes-stats,
+.paginationIndex .images-stats {
+ font-size: 0;
+ color: var(--vel-t3);
+}
+.paginationIndex .scenes-stats > span,
+.paginationIndex .images-stats > span {
+ font-size: 11px;
+}
+.paginationIndex .scenes-stats > span::before,
+.paginationIndex .images-stats > span::before {
+ content: " · ";
+}
+
+/* ---------- Filter sidebar ---------- */
+.sidebar-pane .sidebar,
+.sidebar .sidebar-footer,
+.sidebar-toolbar {
+ background-color: var(--vel-bg1) !important;
+}
+.sidebar-pane .sidebar {
+ border-right: 1px solid var(--vel-line);
+ padding: 12px 12px 0 !important;
+}
+.sidebar .sidebar-section,
+.sidebar .sidebar-list-filter {
+ border: 0 !important;
+}
+.sidebar .collapse-button,
+.sidebar .sidebar-section-header,
+.sidebar .btn.minimal {
+ color: var(--vel-t1) !important;
+ font-size: 13px;
+ font-weight: 500;
+ border-radius: 8px !important;
+}
+.sidebar .collapse-button:hover {
+ background-color: var(--vel-bg2) !important;
+}
+.sidebar .collapse-button svg {
+ color: var(--vel-t3);
+}
+
+/* Tables (list view, package manager) */
+.table-list table thead,
+.package-manager table thead {
+ background-color: var(--vel-bg0) !important;
+}
+#tasks-panel .tasks-panel-queue {
+ background-color: var(--vel-bg0) !important;
+}
+
+/* ---------- Rows under the toolbar: filter chips, pagination, count ----------
+ One 10px rhythm between toolbar, chips, pagination and grid. On wide
+ screens the result count leaves its own line and sits right-aligned on the
+ first row under the toolbar (the filter chips if any, else pagination). */
+.sidebar-pane-content > .filter-tags {
+ margin: 10px 0 0 !important;
+ padding: 0 16px;
+ justify-content: center;
+}
+.sidebar-pane-content > .pagination-index-container {
+ margin-top: 10px !important;
+}
+.sidebar-pane-content > .pagination-index-container:not(:has(.pagination)) {
+ margin-top: 0 !important;
+}
+.row.justify-content-center:has(> .grid-card) {
+ padding-top: 10px !important;
+}
+@media (min-width: 1200px) {
+ .sidebar-pane-content {
+ position: relative;
+ }
+ .sidebar-pane-content > .pagination-index-container > .paginationIndex {
+ position: absolute;
+ right: 16px;
+ top: 49px; /* toolbar bottom (39px) + the 10px gap */
+ height: 32px;
+ margin: 0 !important;
+ display: flex !important;
+ align-items: center;
+ white-space: nowrap;
+ }
+ /* keep centred chips clear of the count on the right */
+ .sidebar-pane-content > .filter-tags {
+ padding: 0 190px;
+ }
+ /* nothing else on the row: hold the space so the grid doesn't slide under the count */
+ .sidebar-pane-content:not(:has(> .filter-tags)) > .pagination-index-container:not(:has(.pagination)) {
+ min-height: 32px;
+ margin-top: 10px !important;
+ }
+}
+@media (min-width: 1200px) {
+ /* on the chip row, match the chips' height so the text centres on them */
+ .sidebar-pane-content:has(> .filter-tags) > .pagination-index-container > .paginationIndex {
+ height: 24px;
+ }
+}
+/* flex grids (studios, tags, performers) carry a 6px card margin; trim the
+ row padding so every grid starts 10px below the row above */
+.row.justify-content-center:has(> .grid-card:not(.scene-card)) {
+ padding-top: 4px !important;
+}
diff --git a/themes/Theme-Velour/tokens.css b/themes/Theme-Velour/tokens.css
new file mode 100644
index 00000000..bcc10eae
--- /dev/null
+++ b/themes/Theme-Velour/tokens.css
@@ -0,0 +1,140 @@
+/* Velour: a velvet lounge. Deep plum that reads as black until lit; dusty
+ rose for actions, champagne for flourishes; italic serif titles over a
+ quiet sans. Framed 16:9 cards.
+
+ Fonts are self-hosted (assets/fonts, OFL) so pages make no external
+ requests. The yml maps the plugin's assets/ folder to
+ /plugin/Theme-Velour/assets/; font URLs are relative to the stylesheet. */
+
+@font-face {
+ font-family: "Outfit";
+ font-style: normal;
+ font-weight: 400 600;
+ font-display: swap;
+ src: url("assets/fonts/Outfit-var.woff2") format("woff2");
+}
+@font-face {
+ font-family: "Instrument Serif";
+ font-style: normal;
+ font-weight: 400;
+ font-display: swap;
+ src: url("assets/fonts/InstrumentSerif-400.woff2") format("woff2");
+}
+@font-face {
+ font-family: "Instrument Serif";
+ font-style: italic;
+ font-weight: 400;
+ font-display: swap;
+ src: url("assets/fonts/InstrumentSerif-400i.woff2") format("woff2");
+}
+
+:root {
+ /* Surfaces, darkest to lightest */
+ --vel-bg0: #17121a;
+ --vel-bg1: #1f1922;
+ --vel-bg2: #28212c;
+ --vel-bg3: #352b3a;
+ --vel-bg4: #413546;
+ --vel-line: rgba(255, 255, 255, 0.07);
+ --vel-line-strong: rgba(255, 255, 255, 0.12);
+
+ /* Text */
+ --vel-t1: #f6eef0;
+ --vel-t2: #b9a9b1;
+ --vel-t3: #7f707a;
+
+ /* Accent: dusty rose for actions, champagne for flourishes */
+ --vel-acc2: #d9b36b;
+ --vel-acc: #d97a8c;
+ --vel-acc-hover: #e48d9d;
+ --vel-acc-ink: #2a0d14;
+ --vel-acc-soft: rgba(217, 122, 140, 0.16);
+ --vel-danger: #c9574f;
+ --vel-rating: #d97a8c;
+
+ /* Type */
+ --vel-font-body: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;
+ --vel-font-title: "Instrument Serif", Georgia, "Times New Roman", serif;
+ --vel-title-style: italic;
+ --vel-title-card: 19px;
+ --vel-title-page: 34px;
+ --vel-title-lh: 1.1;
+
+ /* Shape */
+ --vel-r-ctl: 10px; /* buttons, inputs, control groups */
+ --vel-r-in: 7px; /* segments inside a control group */
+ --vel-r-card: 14px;
+ --vel-r-panel: 14px;
+ --vel-r-chip: 6px;
+ --vel-ctl-h: 34px;
+
+ /* Depth and motion */
+ --vel-shadow-pop: 0 16px 40px rgba(0, 0, 0, 0.55);
+ --vel-glow: 0 10px 28px rgba(217, 122, 140, 0.16);
+ --vel-ring: 0 0 0 2px var(--vel-acc);
+ --vel-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
+ --vel-media: repeating-linear-gradient(135deg, #2c242f 0 8px, #261f29 8px 16px);
+}
+
+html,
+body {
+ background-color: var(--vel-bg0) !important;
+ color: var(--vel-t1);
+ font-family: var(--vel-font-body);
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ font-size: 14px;
+ line-height: 1.45;
+}
+
+a {
+ color: var(--vel-t1);
+}
+a:hover {
+ color: #fff;
+}
+
+h1, h2, h3, h4,
+.card-section-title,
+.modal-title {
+ font-family: var(--vel-font-title);
+ font-style: var(--vel-title-style);
+ font-weight: 400;
+ letter-spacing: 0;
+}
+
+::selection {
+ background: var(--vel-acc-soft);
+}
+
+/* Quiet scrollbars */
+* {
+ scrollbar-color: var(--vel-bg3) transparent;
+ scrollbar-width: thin;
+}
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+::-webkit-scrollbar-thumb {
+ background: var(--vel-bg3);
+ border: 3px solid var(--vel-bg0);
+ border-radius: 999px;
+}
+
+:focus-visible {
+ outline: 2px solid var(--vel-acc) !important;
+ outline-offset: 2px;
+}
+
+.text-muted {
+ color: var(--vel-t3) !important;
+}
+hr {
+ border-color: var(--vel-line);
+}