domma-js 0.48.0 → 0.50.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-js",
3
- "version": "0.48.0",
3
+ "version": "0.50.0",
4
4
  "description": "Dynamic Object Manipulation & Modeling API - A complete front-end toolkit.",
5
5
  "main": "public/dist/domma.min.js",
6
6
  "module": "public/dist/domma.esm.js",
@@ -1,8 +1,8 @@
1
1
  /*!
2
- * Domma Complete CSS Bundle v0.48.0
2
+ * Domma Complete CSS Bundle v0.50.0
3
3
  * Dynamic Object Manipulation & Modeling API
4
4
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5
- * Built: 2026-09-27T22:11:07.262Z
5
+ * Built: 2026-09-28T07:48:40.116Z
6
6
  */
7
7
 
8
8
  /* ============================================
@@ -11,11 +11,11 @@
11
11
  ============================================ */
12
12
 
13
13
  /*!
14
- * Domma Core CSS v0.48.0
14
+ * Domma Core CSS v0.50.0
15
15
  * Dynamic Object Manipulation & Modeling API
16
16
  * (c) 2026 Darryl Waterhouse & DCBW-IT
17
- * Built: 2026-09-27T22:11:07.093Z
18
- * Commit: cf6d8de
17
+ * Built: 2026-09-28T07:48:39.949Z
18
+ * Commit: d400172
19
19
  */
20
20
 
21
21
  /**
@@ -1216,8 +1216,9 @@ code, pre {
1216
1216
  letter-spacing: 0.025em;
1217
1217
  }
1218
1218
 
1219
- /* Truncate */
1220
- .truncate {
1219
+ /* Truncate - one line with an ellipsis. .text-truncate is the Bootstrap spelling. */
1220
+ .truncate,
1221
+ .text-truncate {
1221
1222
  overflow: hidden;
1222
1223
  text-overflow: ellipsis;
1223
1224
  white-space: nowrap;
@@ -2192,6 +2193,143 @@ code, pre {
2192
2193
  user-select: text;
2193
2194
  }
2194
2195
 
2196
+ /* ============================================
2197
+ ACCESSIBILITY & TEXT UTILITIES
2198
+ Screen-reader-only text, skip links, line clamping,
2199
+ whole-card links and a divider with a word in it.
2200
+ Tailwind names (.sr-only, .line-clamp-*) with the Bootstrap
2201
+ spellings (.visually-hidden, .text-truncate) as aliases.
2202
+ ============================================ */
2203
+
2204
+ /* Hidden on screen, still read by screen readers. The -focusable form comes
2205
+ back when it (or something inside it) takes focus - the skip link pattern. */
2206
+ .sr-only,
2207
+ .visually-hidden,
2208
+ .sr-only-focusable:not(:focus):not(:focus-within),
2209
+ .visually-hidden-focusable:not(:focus):not(:focus-within),
2210
+ .skip-link:not(:focus):not(:focus-within) {
2211
+ position: absolute !important;
2212
+ width: 1px !important;
2213
+ height: 1px !important;
2214
+ padding: 0 !important;
2215
+ margin: -1px !important;
2216
+ overflow: hidden !important;
2217
+ clip: rect(0, 0, 0, 0) !important;
2218
+ clip-path: inset(50%) !important;
2219
+ white-space: nowrap !important;
2220
+ border: 0 !important;
2221
+ }
2222
+
2223
+ /* A ready-made skip link: the first thing in <body>, hidden until Tab reaches
2224
+ it, then pinned top left above everything (popovers are 10045). */
2225
+ .skip-link:focus,
2226
+ .skip-link:focus-within {
2227
+ position: fixed;
2228
+ top: var(--dm-space-3);
2229
+ left: var(--dm-space-3);
2230
+ z-index: 10100;
2231
+ padding: var(--dm-space-2) var(--dm-space-4);
2232
+ border-radius: var(--dm-radius-md);
2233
+ background-color: var(--dm-primary);
2234
+ color: var(--dm-primary-text, var(--dm-white));
2235
+ font-weight: 600;
2236
+ text-decoration: none;
2237
+ box-shadow: var(--dm-shadow-lg);
2238
+ outline: 2px solid var(--dm-border-focus);
2239
+ outline-offset: 2px;
2240
+ }
2241
+
2242
+ /* Undo .sr-only, e.g. at a breakpoint or from a script */
2243
+ .not-sr-only {
2244
+ position: static !important;
2245
+ width: auto !important;
2246
+ height: auto !important;
2247
+ padding: 0 !important;
2248
+ margin: 0 !important;
2249
+ overflow: visible !important;
2250
+ clip: auto !important;
2251
+ clip-path: none !important;
2252
+ white-space: normal !important;
2253
+ }
2254
+
2255
+ /* Line clamp - cut text after N lines with an ellipsis */
2256
+ .line-clamp-1,
2257
+ .line-clamp-2,
2258
+ .line-clamp-3,
2259
+ .line-clamp-4,
2260
+ .line-clamp-5 {
2261
+ display: -webkit-box;
2262
+ overflow: hidden;
2263
+ -webkit-box-orient: vertical;
2264
+ }
2265
+
2266
+ .line-clamp-1 { -webkit-line-clamp: 1; }
2267
+ .line-clamp-2 { -webkit-line-clamp: 2; }
2268
+ .line-clamp-3 { -webkit-line-clamp: 3; }
2269
+ .line-clamp-4 { -webkit-line-clamp: 4; }
2270
+ .line-clamp-5 { -webkit-line-clamp: 5; }
2271
+
2272
+ .line-clamp-none {
2273
+ display: block;
2274
+ overflow: visible;
2275
+ -webkit-box-orient: horizontal;
2276
+ -webkit-line-clamp: unset;
2277
+ }
2278
+
2279
+ /* Stretched link - the link's ::after covers its nearest positioned ancestor,
2280
+ so the whole card is the link. A .card holding one is positioned for you;
2281
+ anything else needs .relative. Other links or buttons inside must sit above
2282
+ it: give them .relative .z-10. */
2283
+ .stretched-link::after {
2284
+ content: "";
2285
+ position: absolute;
2286
+ inset: 0;
2287
+ z-index: 1;
2288
+ border-radius: inherit;
2289
+ }
2290
+
2291
+ .card:has(.stretched-link),
2292
+ .list-group-item:has(.stretched-link) {
2293
+ position: relative;
2294
+ }
2295
+
2296
+ .stretched-link:focus-visible {
2297
+ outline: none;
2298
+ }
2299
+
2300
+ .stretched-link:focus-visible::after {
2301
+ outline: 2px solid var(--dm-border-focus);
2302
+ outline-offset: -2px;
2303
+ }
2304
+
2305
+ /* Divider with text - a rule either side of a word, e.g. "or" between two
2306
+ sign-in options. -start / -end push the text to one side. */
2307
+ .divider-text {
2308
+ display: flex;
2309
+ align-items: center;
2310
+ gap: var(--dm-space-3);
2311
+ margin: var(--dm-space-4) 0;
2312
+ color: var(--dm-text-secondary);
2313
+ font-size: var(--dm-font-size-sm);
2314
+ line-height: 1.4;
2315
+ }
2316
+
2317
+ .divider-text::before,
2318
+ .divider-text::after {
2319
+ content: "";
2320
+ flex: 1 1 0;
2321
+ border-top: 1px solid var(--dm-border);
2322
+ }
2323
+
2324
+ .divider-text:empty {
2325
+ gap: 0;
2326
+ }
2327
+
2328
+ .divider-text-start::before,
2329
+ .divider-text-end::after {
2330
+ flex: 0 0 var(--dm-space-6);
2331
+ }
2332
+
2195
2333
  /* ============================================
2196
2334
  OPACITY SCALE
2197
2335
  Full graduated opacity scale (0-100)
@@ -4835,11 +4973,11 @@ body.dm-cloaked.dm-ready {
4835
4973
  ============================================ */
4836
4974
 
4837
4975
  /*!
4838
- * Domma Grid CSS v0.48.0
4976
+ * Domma Grid CSS v0.50.0
4839
4977
  * Dynamic Object Manipulation & Modeling API
4840
4978
  * (c) 2026 Darryl Waterhouse & DCBW-IT
4841
- * Built: 2026-09-27T22:11:07.097Z
4842
- * Commit: cf6d8de
4979
+ * Built: 2026-09-28T07:48:39.953Z
4980
+ * Commit: d400172
4843
4981
  */
4844
4982
 
4845
4983
  /**
@@ -5460,11 +5598,11 @@ body.dm-cloaked.dm-ready {
5460
5598
  ============================================ */
5461
5599
 
5462
5600
  /*!
5463
- * Domma Elements CSS v0.48.0
5601
+ * Domma Elements CSS v0.50.0
5464
5602
  * Dynamic Object Manipulation & Modeling API
5465
5603
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5466
- * Built: 2026-09-27T22:11:07.101Z
5467
- * Commit: cf6d8de
5604
+ * Built: 2026-09-28T07:48:39.958Z
5605
+ * Commit: d400172
5468
5606
  */
5469
5607
 
5470
5608
  /**
@@ -14518,6 +14656,455 @@ td > .skeleton-text {
14518
14656
  }
14519
14657
  }
14520
14658
 
14659
+ /* ============================================
14660
+ POPOVER
14661
+ E.popover(trigger, options) - src/popover.js
14662
+ ============================================ */
14663
+
14664
+ /*
14665
+ * Portalled to <body> and positioned with `fixed`, so no ancestor's overflow
14666
+ * or stacking context can clip it. Above modals and slideovers (a popover is
14667
+ * usually opened from inside one), below the context menu (10050), which can
14668
+ * be opened from inside a popover.
14669
+ *
14670
+ * Width options arrive as custom properties, so a theme or page stylesheet
14671
+ * can still set them without fighting inline styles.
14672
+ */
14673
+ .dm-popover {
14674
+ position: fixed;
14675
+ top: 0;
14676
+ left: 0;
14677
+ z-index: 10045;
14678
+ box-sizing: border-box;
14679
+ display: flex;
14680
+ flex-direction: column;
14681
+ width: var(--dm-popover-width, auto);
14682
+ max-width: min(var(--dm-popover-max-width, 20rem), calc(100vw - 16px));
14683
+ max-height: calc(100vh - 16px);
14684
+ background: var(--dm-surface);
14685
+ color: var(--dm-text);
14686
+ border: 1px solid var(--dm-border);
14687
+ border-radius: var(--dm-radius-md);
14688
+ box-shadow: var(--dm-shadow-lg);
14689
+ font-size: 0.875rem;
14690
+ line-height: 1.5;
14691
+ text-align: left;
14692
+ white-space: normal;
14693
+ overflow-wrap: break-word;
14694
+ opacity: 0;
14695
+ transform: scale(0.96);
14696
+ transform-origin: top center;
14697
+ transition:
14698
+ opacity var(--dm-popover-duration, 150ms) ease,
14699
+ transform var(--dm-popover-duration, 150ms) cubic-bezier(0.16, 1, 0.3, 1);
14700
+ outline: none;
14701
+ }
14702
+
14703
+ .dm-popover[data-side="top"] { transform-origin: bottom center; }
14704
+ .dm-popover[data-side="bottom"] { transform-origin: top center; }
14705
+ .dm-popover[data-side="left"] { transform-origin: center right; }
14706
+ .dm-popover[data-side="right"] { transform-origin: center left; }
14707
+
14708
+ .dm-popover.is-open {
14709
+ opacity: 1;
14710
+ transform: none;
14711
+ }
14712
+
14713
+ .dm-popover[data-animation="off"] {
14714
+ transition: none;
14715
+ }
14716
+
14717
+ /* The trigger has scrolled out of view; the panel waits for it. */
14718
+ .dm-popover.is-detached {
14719
+ visibility: hidden;
14720
+ pointer-events: none;
14721
+ }
14722
+
14723
+ .dm-popover [hidden] {
14724
+ display: none;
14725
+ }
14726
+
14727
+ .dm-popover-header {
14728
+ padding: 0.625rem 0.875rem 0;
14729
+ }
14730
+
14731
+ .dm-popover-title {
14732
+ margin: 0;
14733
+ font-size: 0.9375rem;
14734
+ font-weight: 600;
14735
+ line-height: 1.35;
14736
+ color: var(--dm-text);
14737
+ }
14738
+
14739
+ /* The body scrolls when the content is taller than the viewport, so the
14740
+ panel itself keeps overflow visible and the arrow is not clipped. */
14741
+ .dm-popover-body {
14742
+ min-height: 0;
14743
+ padding: 0.625rem 0.875rem;
14744
+ overflow: auto;
14745
+ }
14746
+
14747
+ .dm-popover-body > :first-child { margin-top: 0; }
14748
+ .dm-popover-body > :last-child { margin-bottom: 0; }
14749
+
14750
+ .dm-popover.is-dismissible .dm-popover-header,
14751
+ .dm-popover.is-dismissible:not(.has-title) .dm-popover-body {
14752
+ padding-right: 2.25rem;
14753
+ }
14754
+
14755
+ .dm-popover-close {
14756
+ position: absolute;
14757
+ top: 0.375rem;
14758
+ right: 0.375rem;
14759
+ display: inline-flex;
14760
+ align-items: center;
14761
+ justify-content: center;
14762
+ width: 1.5rem;
14763
+ height: 1.5rem;
14764
+ padding: 0;
14765
+ border: 0;
14766
+ border-radius: var(--dm-radius-sm);
14767
+ background: transparent;
14768
+ color: var(--dm-text);
14769
+ cursor: pointer;
14770
+ }
14771
+
14772
+ .dm-popover-close:hover {
14773
+ background: var(--dm-hover-bg);
14774
+ color: var(--dm-text);
14775
+ }
14776
+
14777
+ .dm-popover-close:focus-visible {
14778
+ outline: 2px solid var(--dm-primary);
14779
+ outline-offset: 1px;
14780
+ }
14781
+
14782
+ /*
14783
+ * A square turned 45 degrees, bordered on the two edges that face out, so it
14784
+ * reads as a continuation of the panel's own border. JS sets its position
14785
+ * along the edge (left for top/bottom, top for left/right).
14786
+ */
14787
+ .dm-popover-arrow {
14788
+ position: absolute;
14789
+ width: 10px;
14790
+ height: 10px;
14791
+ margin: -5px 0 0 -5px;
14792
+ background: var(--dm-surface);
14793
+ border: 1px solid var(--dm-border);
14794
+ transform: rotate(45deg);
14795
+ pointer-events: none;
14796
+ }
14797
+
14798
+ .dm-popover[data-side="bottom"] .dm-popover-arrow {
14799
+ top: -1px;
14800
+ border-right: 0;
14801
+ border-bottom: 0;
14802
+ }
14803
+
14804
+ .dm-popover[data-side="top"] .dm-popover-arrow {
14805
+ top: calc(100% + 1px);
14806
+ border-left: 0;
14807
+ border-top: 0;
14808
+ }
14809
+
14810
+ .dm-popover[data-side="right"] .dm-popover-arrow {
14811
+ left: -1px;
14812
+ margin: -5px 0 0 -5px;
14813
+ border-top: 0;
14814
+ border-right: 0;
14815
+ }
14816
+
14817
+ .dm-popover[data-side="left"] .dm-popover-arrow {
14818
+ left: calc(100% + 1px);
14819
+ border-bottom: 0;
14820
+ border-left: 0;
14821
+ }
14822
+
14823
+ .dm-popover-trigger[role="button"] {
14824
+ cursor: pointer;
14825
+ }
14826
+
14827
+ @media (prefers-reduced-motion: reduce) {
14828
+ .dm-popover {
14829
+ transition: none;
14830
+ transform: none;
14831
+ }
14832
+ }
14833
+
14834
+ /* ============================================
14835
+ AVATAR
14836
+ ============================================
14837
+ * A person's picture, initials or an icon. Sizes set --dm-avatar-size, which
14838
+ * everything else (font, dot, overlap) scales from; shapes set
14839
+ * --dm-avatar-radius. On a group the size sits on the list and the avatars
14840
+ * inherit it.
14841
+ *
14842
+ * Tones: eight hues mixed 24% into the theme surface, the initials the same hue
14843
+ * mixed 30% into the theme text. Following the theme on both sides is what keeps
14844
+ * them AA everywhere - the lowest measured pair across all 39 themes is 5.2:1.
14845
+ *
14846
+ * --dm-avatar-gap-color is the ring that separates stacked avatars and circles
14847
+ * the status dot: the page background by default, the card inside a card.
14848
+ */
14849
+ .avatar {
14850
+ position: relative;
14851
+ box-sizing: border-box;
14852
+ display: inline-flex;
14853
+ flex-shrink: 0;
14854
+ align-items: center;
14855
+ justify-content: center;
14856
+ width: var(--dm-avatar-size, 2.5rem);
14857
+ height: var(--dm-avatar-size, 2.5rem);
14858
+ padding: 0;
14859
+ border: 0;
14860
+ border-radius: var(--dm-avatar-radius, var(--dm-radius-full));
14861
+ background-color: color-mix(in srgb, var(--dm-text) 12%, var(--dm-surface));
14862
+ color: var(--dm-text);
14863
+ font-family: inherit;
14864
+ font-size: calc(var(--dm-avatar-size, 2.5rem) * 0.4);
14865
+ font-weight: 600;
14866
+ line-height: 1;
14867
+ text-decoration: none;
14868
+ vertical-align: middle;
14869
+ user-select: none;
14870
+ }
14871
+
14872
+ a.avatar,
14873
+ button.avatar {
14874
+ cursor: pointer;
14875
+ }
14876
+
14877
+ /* The global a:hover colour would otherwise repaint a linked avatar's initials */
14878
+ a.avatar:hover {
14879
+ background-color: color-mix(in srgb, var(--dm-text) 12%, var(--dm-surface));
14880
+ color: var(--dm-text);
14881
+ }
14882
+
14883
+ a.avatar:focus-visible,
14884
+ button.avatar:focus-visible {
14885
+ outline: 2px solid var(--dm-border-focus);
14886
+ outline-offset: 2px;
14887
+ }
14888
+
14889
+ .avatar-img {
14890
+ display: block;
14891
+ width: 100%;
14892
+ height: 100%;
14893
+ object-fit: cover;
14894
+ border-radius: inherit;
14895
+ }
14896
+
14897
+ .avatar-initials {
14898
+ letter-spacing: 0.02em;
14899
+ white-space: nowrap;
14900
+ }
14901
+
14902
+ .avatar-icon {
14903
+ display: inline-flex;
14904
+ }
14905
+
14906
+ /* Domma.icons.scan() swaps the span for the svg and keeps its classes */
14907
+ svg.avatar-icon,
14908
+ .avatar-icon svg {
14909
+ width: calc(var(--dm-avatar-size, 2.5rem) * 0.55);
14910
+ height: calc(var(--dm-avatar-size, 2.5rem) * 0.55);
14911
+ }
14912
+
14913
+ /* Sizes */
14914
+ .avatar-xs, .avatar-group-xs { --dm-avatar-size: 1.5rem; }
14915
+ .avatar-sm, .avatar-group-sm { --dm-avatar-size: 2rem; }
14916
+ .avatar-md, .avatar-group-md { --dm-avatar-size: 2.5rem; }
14917
+ .avatar-lg, .avatar-group-lg { --dm-avatar-size: 3.5rem; }
14918
+ .avatar-xl, .avatar-group-xl { --dm-avatar-size: 5rem; }
14919
+
14920
+ /* Shapes - circle is the default */
14921
+ .avatar-rounded { --dm-avatar-radius: calc(var(--dm-avatar-size, 2.5rem) * 0.22); }
14922
+ .avatar-square { --dm-avatar-radius: 0; }
14923
+
14924
+ /* Tones, picked from the name by E.avatar(). The a:hover form keeps a linked
14925
+ avatar's initials their own colour under the global link hover. */
14926
+ .avatar-tone-0,
14927
+ a.avatar-tone-0:hover {
14928
+ background-color: color-mix(in srgb, var(--dm-red-500) 24%, var(--dm-surface));
14929
+ color: color-mix(in srgb, var(--dm-red-500) 30%, var(--dm-text));
14930
+ }
14931
+
14932
+ .avatar-tone-1,
14933
+ a.avatar-tone-1:hover {
14934
+ background-color: color-mix(in srgb, color-mix(in srgb, var(--dm-red-500) 50%, var(--dm-amber-500)) 24%, var(--dm-surface));
14935
+ color: color-mix(in srgb, color-mix(in srgb, var(--dm-red-500) 50%, var(--dm-amber-500)) 30%, var(--dm-text));
14936
+ }
14937
+
14938
+ .avatar-tone-2,
14939
+ a.avatar-tone-2:hover {
14940
+ background-color: color-mix(in srgb, var(--dm-amber-500) 24%, var(--dm-surface));
14941
+ color: color-mix(in srgb, var(--dm-amber-500) 30%, var(--dm-text));
14942
+ }
14943
+
14944
+ .avatar-tone-3,
14945
+ a.avatar-tone-3:hover {
14946
+ background-color: color-mix(in srgb, var(--dm-green-500) 24%, var(--dm-surface));
14947
+ color: color-mix(in srgb, var(--dm-green-500) 30%, var(--dm-text));
14948
+ }
14949
+
14950
+ .avatar-tone-4,
14951
+ a.avatar-tone-4:hover {
14952
+ background-color: color-mix(in srgb, color-mix(in srgb, var(--dm-green-500) 50%, var(--dm-sky-500)) 24%, var(--dm-surface));
14953
+ color: color-mix(in srgb, color-mix(in srgb, var(--dm-green-500) 50%, var(--dm-sky-500)) 30%, var(--dm-text));
14954
+ }
14955
+
14956
+ .avatar-tone-5,
14957
+ a.avatar-tone-5:hover {
14958
+ background-color: color-mix(in srgb, var(--dm-sky-500) 24%, var(--dm-surface));
14959
+ color: color-mix(in srgb, var(--dm-sky-500) 30%, var(--dm-text));
14960
+ }
14961
+
14962
+ .avatar-tone-6,
14963
+ a.avatar-tone-6:hover {
14964
+ background-color: color-mix(in srgb, var(--dm-blue-500) 24%, var(--dm-surface));
14965
+ color: color-mix(in srgb, var(--dm-blue-500) 30%, var(--dm-text));
14966
+ }
14967
+
14968
+ .avatar-tone-7,
14969
+ a.avatar-tone-7:hover {
14970
+ background-color: color-mix(in srgb, color-mix(in srgb, var(--dm-blue-500) 55%, var(--dm-red-500)) 24%, var(--dm-surface));
14971
+ color: color-mix(in srgb, color-mix(in srgb, var(--dm-blue-500) 55%, var(--dm-red-500)) 30%, var(--dm-text));
14972
+ }
14973
+
14974
+ /* Where the ring colour comes from */
14975
+ .card {
14976
+ --dm-avatar-gap-color: var(--dm-card-bg, var(--dm-surface));
14977
+ }
14978
+
14979
+ .dm-popover {
14980
+ --dm-avatar-gap-color: var(--dm-surface);
14981
+ }
14982
+
14983
+ /* Ring */
14984
+ .avatar-ring {
14985
+ box-shadow: 0 0 0 2px var(--dm-avatar-gap-color, var(--dm-background)), 0 0 0 4px var(--dm-primary);
14986
+ }
14987
+
14988
+ /* Status dot. Offline is hollow and busy carries a bar, so colour is not the
14989
+ only difference; the word itself is in the avatar's accessible name. */
14990
+ .avatar-status {
14991
+ position: absolute;
14992
+ right: 0;
14993
+ bottom: 0;
14994
+ box-sizing: border-box;
14995
+ width: max(0.5rem, calc(var(--dm-avatar-size, 2.5rem) * 0.28));
14996
+ height: max(0.5rem, calc(var(--dm-avatar-size, 2.5rem) * 0.28));
14997
+ border-radius: var(--dm-radius-full);
14998
+ background-color: var(--dm-text-muted);
14999
+ box-shadow: 0 0 0 2px var(--dm-avatar-gap-color, var(--dm-background));
15000
+ }
15001
+
15002
+ .avatar-status-online { background-color: var(--dm-success); }
15003
+ .avatar-status-away { background-color: var(--dm-warning); }
15004
+ .avatar-status-busy { background-color: var(--dm-danger); }
15005
+
15006
+ .avatar-status-busy::after {
15007
+ content: "";
15008
+ position: absolute;
15009
+ top: 50%;
15010
+ left: 22%;
15011
+ right: 22%;
15012
+ height: 2px;
15013
+ margin-top: -1px;
15014
+ border-radius: 1px;
15015
+ background-color: var(--dm-avatar-gap-color, var(--dm-background));
15016
+ }
15017
+
15018
+ .avatar-status-offline {
15019
+ background-color: var(--dm-avatar-gap-color, var(--dm-background));
15020
+ border: 2px solid var(--dm-text-muted);
15021
+ }
15022
+
15023
+ .avatar-square .avatar-status,
15024
+ .avatar-rounded .avatar-status {
15025
+ right: -0.125rem;
15026
+ bottom: -0.125rem;
15027
+ }
15028
+
15029
+ /* Group: a list of overlapping avatars */
15030
+ .avatar-group {
15031
+ isolation: isolate; /* the items' z-index stays inside the group, under navbars and menus */
15032
+ display: inline-flex;
15033
+ flex-direction: row;
15034
+ align-items: center;
15035
+ margin: 0;
15036
+ padding: 0;
15037
+ list-style: none;
15038
+ }
15039
+
15040
+ /* E.avatarGroup() sets --dm-avatar-z on each item, highest first, so each
15041
+ avatar sits over the next and its status dot stays in view. Hand-written
15042
+ groups stack the other way (later on top) unless they set it too. */
15043
+ .avatar-group > * {
15044
+ position: relative;
15045
+ z-index: var(--dm-avatar-z, auto);
15046
+ display: inline-flex;
15047
+ margin: 0;
15048
+ padding: 0;
15049
+ }
15050
+
15051
+ .avatar-group > * + * {
15052
+ margin-inline-start: calc(-1 * var(--dm-avatar-overlap, calc(var(--dm-avatar-size, 2.5rem) * 0.25)));
15053
+ }
15054
+
15055
+ .avatar-group .avatar {
15056
+ box-shadow: 0 0 0 2px var(--dm-avatar-gap-color, var(--dm-background));
15057
+ }
15058
+
15059
+ .avatar-group .avatar-ring {
15060
+ box-shadow: 0 0 0 2px var(--dm-avatar-gap-color, var(--dm-background)), 0 0 0 4px var(--dm-primary);
15061
+ }
15062
+
15063
+ .avatar-group > *:hover,
15064
+ .avatar-group > *:focus-within {
15065
+ z-index: 1000;
15066
+ }
15067
+
15068
+ @media (prefers-reduced-motion: no-preference) {
15069
+ .avatar-group .avatar {
15070
+ transition: transform var(--dm-transition-fast);
15071
+ }
15072
+
15073
+ .avatar-group .avatar:hover,
15074
+ .avatar-group .avatar:focus-visible {
15075
+ transform: translateY(-3px);
15076
+ }
15077
+ }
15078
+
15079
+ /* The "+N" bubble */
15080
+ .avatar-more {
15081
+ background-color: color-mix(in srgb, var(--dm-text) 16%, var(--dm-surface));
15082
+ color: var(--dm-text);
15083
+ font-size: calc(var(--dm-avatar-size, 2.5rem) * 0.34);
15084
+ font-weight: 600;
15085
+ }
15086
+
15087
+ .avatar-more-list {
15088
+ display: flex;
15089
+ flex-direction: column;
15090
+ gap: var(--dm-space-1);
15091
+ margin: 0;
15092
+ padding: 0;
15093
+ list-style: none;
15094
+ }
15095
+
15096
+ .avatar-more-person {
15097
+ display: flex;
15098
+ align-items: center;
15099
+ gap: var(--dm-space-2);
15100
+ color: inherit;
15101
+ text-decoration: none;
15102
+ }
15103
+
15104
+ a.avatar-more-person:hover .avatar-more-name {
15105
+ text-decoration: underline;
15106
+ }
15107
+
14521
15108
 
14522
15109
  /* ============================================
14523
15110
  THEME SYSTEM
@@ -14525,11 +15112,11 @@ td > .skeleton-text {
14525
15112
  ============================================ */
14526
15113
 
14527
15114
  /*!
14528
- * Domma Themes v0.48.0
15115
+ * Domma Themes v0.50.0
14529
15116
  * Dynamic Object Manipulation & Modeling API
14530
15117
  * (c) 2026 Darryl Waterhouse & DCBW-IT
14531
- * Built: 2026-09-27T22:11:07.079Z
14532
- * Commit: cf6d8de
15118
+ * Built: 2026-09-28T07:48:39.933Z
15119
+ * Commit: d400172
14533
15120
  */
14534
15121
 
14535
15122
  /**