@ti-engine/web-framework 1.13.1

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.
Files changed (42) hide show
  1. package/.env +4 -0
  2. package/CHANGELOG.md +274 -0
  3. package/README.md +26 -0
  4. package/bin/build/post-install.js +18 -0
  5. package/bin/localization/web-server-labels.json +28 -0
  6. package/bin/static/.well-known/appspecific/com.chrome.devtools.json +6 -0
  7. package/bin/static/favicon.ico +0 -0
  8. package/bin/static/fragments/components/component-notification-bar.html +21 -0
  9. package/bin/static/fragments/components/component-sidebar-flyout.html +37 -0
  10. package/bin/static/fragments/components/component-sidebar.html +33 -0
  11. package/bin/static/fragments/components/component-tooltip.html +11 -0
  12. package/bin/static/fragments/components/component-topbar.html +6 -0
  13. package/bin/static/fragments/frame-administration.html +3 -0
  14. package/bin/static/fragments/frame-application.html +19 -0
  15. package/bin/static/fragments/frame-dashboard.html +3 -0
  16. package/bin/static/fragments/frame-login.html +105 -0
  17. package/bin/static/fragments/frame-not-found.html +3 -0
  18. package/bin/static/fragments/frame-profile.html +3 -0
  19. package/bin/static/index.html +23 -0
  20. package/bin/static/scripts/lib/alpinejs-csp.min.js +7 -0
  21. package/bin/static/scripts/lib/htmx.min.js +1 -0
  22. package/bin/static/scripts/lib/safe-nonce.min.js +1 -0
  23. package/bin/static/scripts/ti-charts.js +1591 -0
  24. package/bin/static/scripts/ti-framework.css +3195 -0
  25. package/bin/static/scripts/ti-framework.js +1427 -0
  26. package/bin/static/scripts/ti-theme-black-glass.css +216 -0
  27. package/bin/static/scripts/ti-theme-daylight.css +87 -0
  28. package/bin/web-app-manager.js +564 -0
  29. package/bin/web-server.js +604 -0
  30. package/bin/web-server.json +49 -0
  31. package/components/admin-config-handlers.js +92 -0
  32. package/components/auth-manager.js +344 -0
  33. package/components/authorization.js +135 -0
  34. package/components/config-change-notifier.js +98 -0
  35. package/components/config-registry.js +246 -0
  36. package/components/config-service.js +349 -0
  37. package/components/config-store.js +246 -0
  38. package/components/definitions.types.js +26 -0
  39. package/components/session-store.js +111 -0
  40. package/components/user.js +133 -0
  41. package/components/web-handlers.js +765 -0
  42. package/package.json +66 -0
@@ -0,0 +1,3195 @@
1
+ /*
2
+ * Icons are sourced from Heroicons (https://heroicons.com/).
3
+ * The source SVG files live in the project's assets/icons directory.
4
+ * Future edits should target the SVG files directly instead of editing long data URIs here.
5
+ */
6
+
7
+ :root {
8
+ /** General **/
9
+ --ti-font-family: system-ui, -apple-system, segoe ui, roboto, arial, sans-serif;
10
+ --ti-font-size: 14px;
11
+ --ti-gap: 12px;
12
+ --ti-grid-gap: 6px;
13
+ --ti-margin: 12px;
14
+ --ti-padding: 12px;
15
+ --ti-internal-padding: 6px;
16
+ --ti-error-color: rgb(230 96 96 / 95%);
17
+ --ti-icon-size: 24px;
18
+ --ti-icon-color: #a6a6a6;
19
+ --ti-icon-hover-color: #ffffff;
20
+ --ti-icon-color-dark: #353535;
21
+ --ti-icon-hover-color-dark: #4c4c4c;
22
+ /** Widgets **/
23
+ --ti-notification-top: 50px;
24
+ --ti-panel-border-radius: 4px;
25
+ --ti-panel-padding: var(--ti-padding);
26
+ --ti-sub-panel-background: #d9d9d9;
27
+ --ti-border-color: rgba(255, 255, 255, 0.15);
28
+ /** Buttons **/
29
+ --ti-button-gap: 6px;
30
+ --ti-button-radius: 10px;
31
+ --ti-button-size: 44px;
32
+ --ti-button-inline-height: 32px;
33
+ /** Data Fields **/
34
+ --ti-data-field-background-color: #b9b9b9;
35
+ --ti-data-field-border-color: var(--ti-data-field-background-color);
36
+ --ti-data-field-border-radius: 2px;
37
+ --ti-data-field-height: 20px;
38
+ --ti-data-field-padding-h: 12px;
39
+ --ti-data-field-padding-v: 6px;
40
+ --ti-data-field-text-color: #111111;
41
+ --ti-description-color: #353535;
42
+ --ti-input-background-color: #ffffff;
43
+ --ti-input-border-color: #a6a6a6;
44
+ --ti-input-outline-color: #8ba0e3;
45
+ --ti-input-outline-shadow-color: rgba(74, 110, 219, 0.2);
46
+ --ti-label-color: #1f1f1f;
47
+ --ti-label-font-size: 14px;
48
+ --ti-title-color: #1d1d1d;
49
+ --ti-title-font-size: 14px;
50
+ }
51
+
52
+ body {
53
+ font-family: var(--ti-font-family), system-ui;
54
+ position: absolute;
55
+ width: 100%;
56
+ height: 100%;
57
+ margin: 0;
58
+ padding: 0;
59
+ overflow: hidden;
60
+ }
61
+
62
+ .ti-icon {
63
+ display: inline-block;
64
+ width: var(--ti-icon-size, 20px);
65
+ height: var(--ti-icon-size, 20px);
66
+ background-color: currentColor;
67
+ -webkit-mask-image: none;
68
+ mask-image: none;
69
+ -webkit-mask-repeat: no-repeat;
70
+ mask-repeat: no-repeat;
71
+ -webkit-mask-position: center;
72
+ mask-position: center;
73
+ -webkit-mask-size: contain;
74
+ mask-size: contain;
75
+ flex-shrink: 0;
76
+ vertical-align: middle;
77
+ }
78
+
79
+ /* Size modifiers — pick the one closest to the surrounding text. xs/sm/md/lg/xl cover the
80
+ 10–32px range we use across the app. The bare .ti-icon keeps a sensible 20px default. */
81
+ .ti-icon.xs {
82
+ --ti-icon-size: 12px;
83
+ }
84
+
85
+ .ti-icon.sm {
86
+ --ti-icon-size: 14px;
87
+ }
88
+
89
+ .ti-icon.md {
90
+ --ti-icon-size: 16px;
91
+ }
92
+
93
+ .ti-icon.lg {
94
+ --ti-icon-size: 24px;
95
+ }
96
+
97
+ .ti-icon.xl {
98
+ --ti-icon-size: 32px;
99
+ }
100
+
101
+ /* Legacy color modifiers preserved for the user-profile flyout (which still uses the explicit
102
+ gray on a dark sidebar). New screens inherit color from their parent automatically. */
103
+ .ti-icon.legacy-gray {
104
+ background-color: var(--ti-icon-color);
105
+ }
106
+
107
+ .ti-icon.legacy-gray:hover {
108
+ background-color: var(--ti-icon-hover-color);
109
+ }
110
+
111
+ .ti-icon.dark-style {
112
+ background-color: var(--ti-icon-color-dark);
113
+ }
114
+
115
+ .ti-icon.dark-style:hover {
116
+ background-color: var(--ti-icon-hover-color-dark);
117
+ }
118
+
119
+ /*
120
+ * -webkit-mask-image: url("data:image/svg+xml,");
121
+ * mask-image: url("data:image/svg+xml,");
122
+ */
123
+ .ti-icon.empty {
124
+ -webkit-mask-image: none;
125
+ mask-image: none;
126
+ background-color: transparent;
127
+ }
128
+
129
+ .ti-icon.app-menu {
130
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M3.75%2012h16.5m-16.5%203.75h16.5M3.75%2019.5h16.5M5.625%204.5h12.75a1.875%201.875%200%200%201%200%203.75H5.625a1.875%201.875%200%200%201%200-3.75Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
131
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M3.75%2012h16.5m-16.5%203.75h16.5M3.75%2019.5h16.5M5.625%204.5h12.75a1.875%201.875%200%200%201%200%203.75H5.625a1.875%201.875%200%200%201%200-3.75Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
132
+ }
133
+
134
+ .ti-icon.app-menu:hover {
135
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M5.625%203.75a2.625%202.625%200%201%200%200%205.25h12.75a2.625%202.625%200%200%200%200-5.25H5.625ZM3.75%2011.25a.75.75%200%200%200%200%201.5h16.5a.75.75%200%200%200%200-1.5H3.75ZM3%2015.75a.75.75%200%200%201%20.75-.75h16.5a.75.75%200%200%201%200%201.5H3.75a.75.75%200%200%201-.75-.75ZM3.75%2018.75a.75.75%200%200%200%200%201.5h16.5a.75.75%200%200%200%200-1.5H3.75Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
136
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M5.625%203.75a2.625%202.625%200%201%200%200%205.25h12.75a2.625%202.625%200%200%200%200-5.25H5.625ZM3.75%2011.25a.75.75%200%200%200%200%201.5h16.5a.75.75%200%200%200%200-1.5H3.75ZM3%2015.75a.75.75%200%200%201%20.75-.75h16.5a.75.75%200%200%201%200%201.5H3.75a.75.75%200%200%201-.75-.75ZM3.75%2018.75a.75.75%200%200%200%200%201.5h16.5a.75.75%200%200%200%200-1.5H3.75Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
137
+ }
138
+
139
+ .ti-icon.dashboard {
140
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22m2.25%2012%208.954-8.955c.44-.439%201.152-.439%201.591%200L21.75%2012M4.5%209.75v10.125c0%20.621.504%201.125%201.125%201.125H9.75v-4.875c0-.621.504-1.125%201.125-1.125h2.25c.621%200%201.125.504%201.125%201.125V21h4.125c.621%200%201.125-.504%201.125-1.125V9.75M8.25%2021h8.25%22%20%2F%3E%0A%3C%2Fsvg%3E");
141
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22m2.25%2012%208.954-8.955c.44-.439%201.152-.439%201.591%200L21.75%2012M4.5%209.75v10.125c0%20.621.504%201.125%201.125%201.125H9.75v-4.875c0-.621.504-1.125%201.125-1.125h2.25c.621%200%201.125.504%201.125%201.125V21h4.125c.621%200%201.125-.504%201.125-1.125V9.75M8.25%2021h8.25%22%20%2F%3E%0A%3C%2Fsvg%3E");
142
+ }
143
+
144
+ .ti-icon.dashboard:hover {
145
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M11.47%203.841a.75.75%200%200%201%201.06%200l8.69%208.69a.75.75%200%201%200%201.06-1.061l-8.689-8.69a2.25%202.25%200%200%200-3.182%200l-8.69%208.69a.75.75%200%201%200%201.061%201.06l8.69-8.689Z%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22m12%205.432%208.159%208.159c.03.03.06.058.091.086v6.198c0%201.035-.84%201.875-1.875%201.875H15a.75.75%200%200%201-.75-.75v-4.5a.75.75%200%200%200-.75-.75h-3a.75.75%200%200%200-.75.75V21a.75.75%200%200%201-.75.75H5.625a1.875%201.875%200%200%201-1.875-1.875v-6.198a2.29%202.29%200%200%200%20.091-.086L12%205.432Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
146
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M11.47%203.841a.75.75%200%200%201%201.06%200l8.69%208.69a.75.75%200%201%200%201.06-1.061l-8.689-8.69a2.25%202.25%200%200%200-3.182%200l-8.69%208.69a.75.75%200%201%200%201.061%201.06l8.69-8.689Z%22%20%2F%3E%0A%20%20%3Cpath%20d%3D%22m12%205.432%208.159%208.159c.03.03.06.058.091.086v6.198c0%201.035-.84%201.875-1.875%201.875H15a.75.75%200%200%201-.75-.75v-4.5a.75.75%200%200%200-.75-.75h-3a.75.75%200%200%200-.75.75V21a.75.75%200%200%201-.75.75H5.625a1.875%201.875%200%200%201-1.875-1.875v-6.198a2.29%202.29%200%200%200%20.091-.086L12%205.432Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
147
+ }
148
+
149
+ .ti-icon.settings {
150
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M9.594%203.94c.09-.542.56-.94%201.11-.94h2.593c.55%200%201.02.398%201.11.94l.213%201.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257%201.075.124l1.217-.456a1.125%201.125%200%200%201%201.37.49l1.296%202.247a1.125%201.125%200%200%201-.26%201.431l-1.003.827c-.293.241-.438.613-.43.992a7.723%207.723%200%200%201%200%20.255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26%201.43l-1.298%202.247a1.125%201.125%200%200%201-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47%206.47%200%200%201-.22.128c-.331.183-.581.495-.644.869l-.213%201.281c-.09.543-.56.94-1.11.94h-2.594c-.55%200-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52%206.52%200%200%201-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125%201.125%200%200%201-1.369-.49l-1.297-2.247a1.125%201.125%200%200%201%20.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932%206.932%200%200%201%200-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125%201.125%200%200%201-.26-1.43l1.297-2.247a1.125%201.125%200%200%201%201.37-.491l1.216.456c.356.133.751.072%201.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z%22%20%2F%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15%2012a3%203%200%201%201-6%200%203%203%200%200%201%206%200Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
151
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M9.594%203.94c.09-.542.56-.94%201.11-.94h2.593c.55%200%201.02.398%201.11.94l.213%201.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257%201.075.124l1.217-.456a1.125%201.125%200%200%201%201.37.49l1.296%202.247a1.125%201.125%200%200%201-.26%201.431l-1.003.827c-.293.241-.438.613-.43.992a7.723%207.723%200%200%201%200%20.255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26%201.43l-1.298%202.247a1.125%201.125%200%200%201-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47%206.47%200%200%201-.22.128c-.331.183-.581.495-.644.869l-.213%201.281c-.09.543-.56.94-1.11.94h-2.594c-.55%200-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52%206.52%200%200%201-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125%201.125%200%200%201-1.369-.49l-1.297-2.247a1.125%201.125%200%200%201%20.26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932%206.932%200%200%201%200-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125%201.125%200%200%201-.26-1.43l1.297-2.247a1.125%201.125%200%200%201%201.37-.491l1.216.456c.356.133.751.072%201.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z%22%20%2F%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15%2012a3%203%200%201%201-6%200%203%203%200%200%201%206%200Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
152
+ }
153
+
154
+ .ti-icon.settings:hover {
155
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M11.078%202.25c-.917%200-1.699.663-1.85%201.567L9.05%204.889c-.02.12-.115.26-.297.348a7.493%207.493%200%200%200-.986.57c-.166.115-.334.126-.45.083L6.3%205.508a1.875%201.875%200%200%200-2.282.819l-.922%201.597a1.875%201.875%200%200%200%20.432%202.385l.84.692c.095.078.17.229.154.43a7.598%207.598%200%200%200%200%201.139c.015.2-.059.352-.153.43l-.841.692a1.875%201.875%200%200%200-.432%202.385l.922%201.597a1.875%201.875%200%200%200%202.282.818l1.019-.382c.115-.043.283-.031.45.082.312.214.641.405.985.57.182.088.277.228.297.35l.178%201.071c.151.904.933%201.567%201.85%201.567h1.844c.916%200%201.699-.663%201.85-1.567l.178-1.072c.02-.12.114-.26.297-.349.344-.165.673-.356.985-.57.167-.114.335-.125.45-.082l1.02.382a1.875%201.875%200%200%200%202.28-.819l.923-1.597a1.875%201.875%200%200%200-.432-2.385l-.84-.692c-.095-.078-.17-.229-.154-.43a7.614%207.614%200%200%200%200-1.139c-.016-.2.059-.352.153-.43l.84-.692c.708-.582.891-1.59.433-2.385l-.922-1.597a1.875%201.875%200%200%200-2.282-.818l-1.02.382c-.114.043-.282.031-.449-.083a7.49%207.49%200%200%200-.985-.57c-.183-.087-.277-.227-.297-.348l-.179-1.072a1.875%201.875%200%200%200-1.85-1.567h-1.843ZM12%2015.75a3.75%203.75%200%201%200%200-7.5%203.75%203.75%200%200%200%200%207.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
156
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M11.078%202.25c-.917%200-1.699.663-1.85%201.567L9.05%204.889c-.02.12-.115.26-.297.348a7.493%207.493%200%200%200-.986.57c-.166.115-.334.126-.45.083L6.3%205.508a1.875%201.875%200%200%200-2.282.819l-.922%201.597a1.875%201.875%200%200%200%20.432%202.385l.84.692c.095.078.17.229.154.43a7.598%207.598%200%200%200%200%201.139c.015.2-.059.352-.153.43l-.841.692a1.875%201.875%200%200%200-.432%202.385l.922%201.597a1.875%201.875%200%200%200%202.282.818l1.019-.382c.115-.043.283-.031.45.082.312.214.641.405.985.57.182.088.277.228.297.35l.178%201.071c.151.904.933%201.567%201.85%201.567h1.844c.916%200%201.699-.663%201.85-1.567l.178-1.072c.02-.12.114-.26.297-.349.344-.165.673-.356.985-.57.167-.114.335-.125.45-.082l1.02.382a1.875%201.875%200%200%200%202.28-.819l.923-1.597a1.875%201.875%200%200%200-.432-2.385l-.84-.692c-.095-.078-.17-.229-.154-.43a7.614%207.614%200%200%200%200-1.139c-.016-.2.059-.352.153-.43l.84-.692c.708-.582.891-1.59.433-2.385l-.922-1.597a1.875%201.875%200%200%200-2.282-.818l-1.02.382c-.114.043-.282.031-.449-.083a7.49%207.49%200%200%200-.985-.57c-.183-.087-.277-.227-.297-.348l-.179-1.072a1.875%201.875%200%200%200-1.85-1.567h-1.843ZM12%2015.75a3.75%203.75%200%201%200%200-7.5%203.75%203.75%200%200%200%200%207.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
157
+ }
158
+
159
+ .ti-icon.error {
160
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%209v3.75m9-.75a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Zm-9%203.75h.008v.008H12v-.008Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
161
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%209v3.75m9-.75a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Zm-9%203.75h.008v.008H12v-.008Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
162
+ }
163
+
164
+ .ti-icon.error:hover {
165
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M2.25%2012c0-5.385%204.365-9.75%209.75-9.75s9.75%204.365%209.75%209.75-4.365%209.75-9.75%209.75S2.25%2017.385%202.25%2012ZM12%208.25a.75.75%200%200%201%20.75.75v3.75a.75.75%200%200%201-1.5%200V9a.75.75%200%200%201%20.75-.75Zm0%208.25a.75.75%200%201%200%200-1.5.75.75%200%200%200%200%201.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
166
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M2.25%2012c0-5.385%204.365-9.75%209.75-9.75s9.75%204.365%209.75%209.75-4.365%209.75-9.75%209.75S2.25%2017.385%202.25%2012ZM12%208.25a.75.75%200%200%201%20.75.75v3.75a.75.75%200%200%201-1.5%200V9a.75.75%200%200%201%20.75-.75Zm0%208.25a.75.75%200%201%200%200-1.5.75.75%200%200%200%200%201.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
167
+ }
168
+
169
+ .ti-icon.user-profile {
170
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15.75%206a3.75%203.75%200%201%201-7.5%200%203.75%203.75%200%200%201%207.5%200ZM4.501%2020.118a7.5%207.5%200%200%201%2014.998%200A17.933%2017.933%200%200%201%2012%2021.75c-2.676%200-5.216-.584-7.499-1.632Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
171
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15.75%206a3.75%203.75%200%201%201-7.5%200%203.75%203.75%200%200%201%207.5%200ZM4.501%2020.118a7.5%207.5%200%200%201%2014.998%200A17.933%2017.933%200%200%201%2012%2021.75c-2.676%200-5.216-.584-7.499-1.632Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
172
+ }
173
+
174
+ .ti-icon.user-profile:hover {
175
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M7.5%206a4.5%204.5%200%201%201%209%200%204.5%204.5%200%200%201-9%200ZM3.751%2020.105a8.25%208.25%200%200%201%2016.498%200%20.75.75%200%200%201-.437.695A18.683%2018.683%200%200%201%2012%2022.5c-2.786%200-5.433-.608-7.812-1.7a.75.75%200%200%201-.437-.695Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
176
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M7.5%206a4.5%204.5%200%201%201%209%200%204.5%204.5%200%200%201-9%200ZM3.751%2020.105a8.25%208.25%200%200%201%2016.498%200%20.75.75%200%200%201-.437.695A18.683%2018.683%200%200%201%2012%2022.5c-2.786%200-5.433-.608-7.812-1.7a.75.75%200%200%201-.437-.695Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
177
+ }
178
+
179
+ .ti-icon.login {
180
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M8.25%209V5.25A2.25%202.25%200%200%201%2010.5%203h6a2.25%202.25%200%200%201%202.25%202.25v13.5A2.25%202.25%200%200%201%2016.5%2021h-6a2.25%202.25%200%200%201-2.25-2.25V15M12%209l3%203m0%200-3%203m3-3H2.25%22%20%2F%3E%0A%3C%2Fsvg%3E");
181
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M8.25%209V5.25A2.25%202.25%200%200%201%2010.5%203h6a2.25%202.25%200%200%201%202.25%202.25v13.5A2.25%202.25%200%200%201%2016.5%2021h-6a2.25%202.25%200%200%201-2.25-2.25V15M12%209l3%203m0%200-3%203m3-3H2.25%22%20%2F%3E%0A%3C%2Fsvg%3E");
182
+ }
183
+
184
+ .ti-icon.login:hover {
185
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M16.5%203.75a1.5%201.5%200%200%201%201.5%201.5v13.5a1.5%201.5%200%200%201-1.5%201.5h-6a1.5%201.5%200%200%201-1.5-1.5V15a.75.75%200%200%200-1.5%200v3.75a3%203%200%200%200%203%203h6a3%203%200%200%200%203-3V5.25a3%203%200%200%200-3-3h-6a3%203%200%200%200-3%203V9A.75.75%200%201%200%209%209V5.25a1.5%201.5%200%200%201%201.5-1.5h6Zm-5.03%204.72a.75.75%200%200%200%200%201.06l1.72%201.72H2.25a.75.75%200%200%200%200%201.5h10.94l-1.72%201.72a.75.75%200%201%200%201.06%201.06l3-3a.75.75%200%200%200%200-1.06l-3-3a.75.75%200%200%200-1.06%200Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
186
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M16.5%203.75a1.5%201.5%200%200%201%201.5%201.5v13.5a1.5%201.5%200%200%201-1.5%201.5h-6a1.5%201.5%200%200%201-1.5-1.5V15a.75.75%200%200%200-1.5%200v3.75a3%203%200%200%200%203%203h6a3%203%200%200%200%203-3V5.25a3%203%200%200%200-3-3h-6a3%203%200%200%200-3%203V9A.75.75%200%201%200%209%209V5.25a1.5%201.5%200%200%201%201.5-1.5h6Zm-5.03%204.72a.75.75%200%200%200%200%201.06l1.72%201.72H2.25a.75.75%200%200%200%200%201.5h10.94l-1.72%201.72a.75.75%200%201%200%201.06%201.06l3-3a.75.75%200%200%200%200-1.06l-3-3a.75.75%200%200%200-1.06%200Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
187
+ }
188
+
189
+ .ti-icon.logout {
190
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15.75%209V5.25A2.25%202.25%200%200%200%2013.5%203h-6a2.25%202.25%200%200%200-2.25%202.25v13.5A2.25%202.25%200%200%200%207.5%2021h6a2.25%202.25%200%200%200%202.25-2.25V15m3%200%203-3m0%200-3-3m3%203H9%22%20%2F%3E%0A%3C%2Fsvg%3E");
191
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M15.75%209V5.25A2.25%202.25%200%200%200%2013.5%203h-6a2.25%202.25%200%200%200-2.25%202.25v13.5A2.25%202.25%200%200%200%207.5%2021h6a2.25%202.25%200%200%200%202.25-2.25V15m3%200%203-3m0%200-3-3m3%203H9%22%20%2F%3E%0A%3C%2Fsvg%3E");
192
+ }
193
+
194
+ .ti-icon.logout:hover {
195
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M7.5%203.75A1.5%201.5%200%200%200%206%205.25v13.5a1.5%201.5%200%200%200%201.5%201.5h6a1.5%201.5%200%200%200%201.5-1.5V15a.75.75%200%200%201%201.5%200v3.75a3%203%200%200%201-3%203h-6a3%203%200%200%201-3-3V5.25a3%203%200%200%201%203-3h6a3%203%200%200%201%203%203V9A.75.75%200%200%201%2015%209V5.25a1.5%201.5%200%200%200-1.5-1.5h-6Zm10.72%204.72a.75.75%200%200%201%201.06%200l3%203a.75.75%200%200%201%200%201.06l-3%203a.75.75%200%201%201-1.06-1.06l1.72-1.72H9a.75.75%200%200%201%200-1.5h10.94l-1.72-1.72a.75.75%200%200%201%200-1.06Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
196
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M7.5%203.75A1.5%201.5%200%200%200%206%205.25v13.5a1.5%201.5%200%200%200%201.5%201.5h6a1.5%201.5%200%200%200%201.5-1.5V15a.75.75%200%200%201%201.5%200v3.75a3%203%200%200%201-3%203h-6a3%203%200%200%201-3-3V5.25a3%203%200%200%201%203-3h6a3%203%200%200%201%203%203V9A.75.75%200%200%201%2015%209V5.25a1.5%201.5%200%200%200-1.5-1.5h-6Zm10.72%204.72a.75.75%200%200%201%201.06%200l3%203a.75.75%200%200%201%200%201.06l-3%203a.75.75%200%201%201-1.06-1.06l1.72-1.72H9a.75.75%200%200%201%200-1.5h10.94l-1.72-1.72a.75.75%200%200%201%200-1.06Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
197
+ }
198
+
199
+ .ti-icon.internet {
200
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%2021a9.004%209.004%200%200%200%208.716-6.747M12%2021a9.004%209.004%200%200%201-8.716-6.747M12%2021c2.485%200%204.5-4.03%204.5-9S14.485%203%2012%203m0%2018c-2.485%200-4.5-4.03-4.5-9S9.515%203%2012%203m0%200a8.997%208.997%200%200%201%207.843%204.582M12%203a8.997%208.997%200%200%200-7.843%204.582m15.686%200A11.953%2011.953%200%200%201%2012%2010.5c-2.998%200-5.74-1.1-7.843-2.918m15.686%200A8.959%208.959%200%200%201%2021%2012c0%20.778-.099%201.533-.284%202.253m0%200A17.919%2017.919%200%200%201%2012%2016.5c-3.162%200-6.133-.815-8.716-2.247m0%200A9.015%209.015%200%200%201%203%2012c0-1.605.42-3.113%201.157-4.418%22%20%2F%3E%0A%3C%2Fsvg%3E");
201
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%2021a9.004%209.004%200%200%200%208.716-6.747M12%2021a9.004%209.004%200%200%201-8.716-6.747M12%2021c2.485%200%204.5-4.03%204.5-9S14.485%203%2012%203m0%2018c-2.485%200-4.5-4.03-4.5-9S9.515%203%2012%203m0%200a8.997%208.997%200%200%201%207.843%204.582M12%203a8.997%208.997%200%200%200-7.843%204.582m15.686%200A11.953%2011.953%200%200%201%2012%2010.5c-2.998%200-5.74-1.1-7.843-2.918m15.686%200A8.959%208.959%200%200%201%2021%2012c0%20.778-.099%201.533-.284%202.253m0%200A17.919%2017.919%200%200%201%2012%2016.5c-3.162%200-6.133-.815-8.716-2.247m0%200A9.015%209.015%200%200%201%203%2012c0-1.605.42-3.113%201.157-4.418%22%20%2F%3E%0A%3C%2Fsvg%3E");
202
+ }
203
+
204
+ .ti-icon.internet:hover {
205
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M21.721%2012.752a9.711%209.711%200%200%200-.945-5.003%2012.754%2012.754%200%200%201-4.339%202.708%2018.991%2018.991%200%200%201-.214%204.772%2017.165%2017.165%200%200%200%205.498-2.477ZM14.634%2015.55a17.324%2017.324%200%200%200%20.332-4.647c-.952.227-1.945.347-2.966.347-1.021%200-2.014-.12-2.966-.347a17.515%2017.515%200%200%200%20.332%204.647%2017.385%2017.385%200%200%200%205.268%200ZM9.772%2017.119a18.963%2018.963%200%200%200%204.456%200A17.182%2017.182%200%200%201%2012%2021.724a17.18%2017.18%200%200%201-2.228-4.605ZM7.777%2015.23a18.87%2018.87%200%200%201-.214-4.774%2012.753%2012.753%200%200%201-4.34-2.708%209.711%209.711%200%200%200-.944%205.004%2017.165%2017.165%200%200%200%205.498%202.477ZM21.356%2014.752a9.765%209.765%200%200%201-7.478%206.817%2018.64%2018.64%200%200%200%201.988-4.718%2018.627%2018.627%200%200%200%205.49-2.098ZM2.644%2014.752c1.682.971%203.53%201.688%205.49%202.099a18.64%2018.64%200%200%200%201.988%204.718%209.765%209.765%200%200%201-7.478-6.816ZM13.878%202.43a9.755%209.755%200%200%201%206.116%203.986%2011.267%2011.267%200%200%201-3.746%202.504%2018.63%2018.63%200%200%200-2.37-6.49ZM12%202.276a17.152%2017.152%200%200%201%202.805%207.121c-.897.23-1.837.353-2.805.353-.968%200-1.908-.122-2.805-.353A17.151%2017.151%200%200%201%2012%202.276ZM10.122%202.43a18.629%2018.629%200%200%200-2.37%206.49%2011.266%2011.266%200%200%201-3.746-2.504%209.754%209.754%200%200%201%206.116-3.985Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
206
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M21.721%2012.752a9.711%209.711%200%200%200-.945-5.003%2012.754%2012.754%200%200%201-4.339%202.708%2018.991%2018.991%200%200%201-.214%204.772%2017.165%2017.165%200%200%200%205.498-2.477ZM14.634%2015.55a17.324%2017.324%200%200%200%20.332-4.647c-.952.227-1.945.347-2.966.347-1.021%200-2.014-.12-2.966-.347a17.515%2017.515%200%200%200%20.332%204.647%2017.385%2017.385%200%200%200%205.268%200ZM9.772%2017.119a18.963%2018.963%200%200%200%204.456%200A17.182%2017.182%200%200%201%2012%2021.724a17.18%2017.18%200%200%201-2.228-4.605ZM7.777%2015.23a18.87%2018.87%200%200%201-.214-4.774%2012.753%2012.753%200%200%201-4.34-2.708%209.711%209.711%200%200%200-.944%205.004%2017.165%2017.165%200%200%200%205.498%202.477ZM21.356%2014.752a9.765%209.765%200%200%201-7.478%206.817%2018.64%2018.64%200%200%200%201.988-4.718%2018.627%2018.627%200%200%200%205.49-2.098ZM2.644%2014.752c1.682.971%203.53%201.688%205.49%202.099a18.64%2018.64%200%200%200%201.988%204.718%209.765%209.765%200%200%201-7.478-6.816ZM13.878%202.43a9.755%209.755%200%200%201%206.116%203.986%2011.267%2011.267%200%200%201-3.746%202.504%2018.63%2018.63%200%200%200-2.37-6.49ZM12%202.276a17.152%2017.152%200%200%201%202.805%207.121c-.897.23-1.837.353-2.805.353-.968%200-1.908-.122-2.805-.353A17.151%2017.151%200%200%201%2012%202.276ZM10.122%202.43a18.629%2018.629%200%200%200-2.37%206.49%2011.266%2011.266%200%200%201-3.746-2.504%209.754%209.754%200%200%201%206.116-3.985Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
207
+ }
208
+
209
+ .ti-icon.calendar {
210
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M6.75%202.994v2.25m10.5-2.25v2.25m-14.252%2013.5V7.491a2.25%202.25%200%200%201%202.25-2.25h13.5a2.25%202.25%200%200%201%202.25%202.25v11.251m-18%200a2.25%202.25%200%200%200%202.25%202.25h13.5a2.25%202.25%200%200%200%202.25-2.25m-18%200v-7.5a2.25%202.25%200%200%201%202.25-2.25h13.5a2.25%202.25%200%200%201%202.25%202.25v7.5m-6.75-6h2.25m-9%202.25h4.5m.002-2.25h.005v.006H12v-.006Zm-.001%204.5h.006v.006h-.006v-.005Zm-2.25.001h.005v.006H9.75v-.006Zm-2.25%200h.005v.005h-.006v-.005Zm6.75-2.247h.005v.005h-.005v-.005Zm0%202.247h.006v.006h-.006v-.006Zm2.25-2.248h.006V15H16.5v-.005Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
211
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M6.75%202.994v2.25m10.5-2.25v2.25m-14.252%2013.5V7.491a2.25%202.25%200%200%201%202.25-2.25h13.5a2.25%202.25%200%200%201%202.25%202.25v11.251m-18%200a2.25%202.25%200%200%200%202.25%202.25h13.5a2.25%202.25%200%200%200%202.25-2.25m-18%200v-7.5a2.25%202.25%200%200%201%202.25-2.25h13.5a2.25%202.25%200%200%201%202.25%202.25v7.5m-6.75-6h2.25m-9%202.25h4.5m.002-2.25h.005v.006H12v-.006Zm-.001%204.5h.006v.006h-.006v-.005Zm-2.25.001h.005v.006H9.75v-.006Zm-2.25%200h.005v.005h-.006v-.005Zm6.75-2.247h.005v.005h-.005v-.005Zm0%202.247h.006v.006h-.006v-.006Zm2.25-2.248h.006V15H16.5v-.005Z%22%20%2F%3E%0A%3C%2Fsvg%3E");
212
+ }
213
+
214
+ .ti-icon.calendar:hover {
215
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M12%2011.993a.75.75%200%200%200-.75.75v.006c0%20.414.336.75.75.75h.006a.75.75%200%200%200%20.75-.75v-.006a.75.75%200%200%200-.75-.75H12ZM12%2016.494a.75.75%200%200%200-.75.75v.005c0%20.414.335.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.005a.75.75%200%200%200-.75-.75H12ZM8.999%2017.244a.75.75%200%200%201%20.75-.75h.006a.75.75%200%200%201%20.75.75v.006a.75.75%200%200%201-.75.75h-.006a.75.75%200%200%201-.75-.75v-.006ZM7.499%2016.494a.75.75%200%200%200-.75.75v.005c0%20.414.336.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.005a.75.75%200%200%200-.75-.75H7.5ZM13.499%2014.997a.75.75%200%200%201%20.75-.75h.006a.75.75%200%200%201%20.75.75v.005a.75.75%200%200%201-.75.75h-.006a.75.75%200%200%201-.75-.75v-.005ZM14.25%2016.494a.75.75%200%200%200-.75.75v.006c0%20.414.335.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.006a.75.75%200%200%200-.75-.75h-.005ZM15.75%2014.995a.75.75%200%200%201%20.75-.75h.005a.75.75%200%200%201%20.75.75v.006a.75.75%200%200%201-.75.75H16.5a.75.75%200%200%201-.75-.75v-.006ZM13.498%2012.743a.75.75%200%200%201%20.75-.75h2.25a.75.75%200%201%201%200%201.5h-2.25a.75.75%200%200%201-.75-.75ZM6.748%2014.993a.75.75%200%200%201%20.75-.75h4.5a.75.75%200%200%201%200%201.5h-4.5a.75.75%200%200%201-.75-.75Z%22%20%2F%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M18%202.993a.75.75%200%200%200-1.5%200v1.5h-9V2.994a.75.75%200%201%200-1.5%200v1.497h-.752a3%203%200%200%200-3%203v11.252a3%203%200%200%200%203%203h13.5a3%203%200%200%200%203-3V7.492a3%203%200%200%200-3-3H18V2.993ZM3.748%2018.743v-7.5a1.5%201.5%200%200%201%201.5-1.5h13.5a1.5%201.5%200%200%201%201.5%201.5v7.5a1.5%201.5%200%200%201-1.5%201.5h-13.5a1.5%201.5%200%200%201-1.5-1.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
216
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20class%3D%22size-6%22%3E%0A%20%20%3Cpath%20d%3D%22M12%2011.993a.75.75%200%200%200-.75.75v.006c0%20.414.336.75.75.75h.006a.75.75%200%200%200%20.75-.75v-.006a.75.75%200%200%200-.75-.75H12ZM12%2016.494a.75.75%200%200%200-.75.75v.005c0%20.414.335.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.005a.75.75%200%200%200-.75-.75H12ZM8.999%2017.244a.75.75%200%200%201%20.75-.75h.006a.75.75%200%200%201%20.75.75v.006a.75.75%200%200%201-.75.75h-.006a.75.75%200%200%201-.75-.75v-.006ZM7.499%2016.494a.75.75%200%200%200-.75.75v.005c0%20.414.336.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.005a.75.75%200%200%200-.75-.75H7.5ZM13.499%2014.997a.75.75%200%200%201%20.75-.75h.006a.75.75%200%200%201%20.75.75v.005a.75.75%200%200%201-.75.75h-.006a.75.75%200%200%201-.75-.75v-.005ZM14.25%2016.494a.75.75%200%200%200-.75.75v.006c0%20.414.335.75.75.75h.005a.75.75%200%200%200%20.75-.75v-.006a.75.75%200%200%200-.75-.75h-.005ZM15.75%2014.995a.75.75%200%200%201%20.75-.75h.005a.75.75%200%200%201%20.75.75v.006a.75.75%200%200%201-.75.75H16.5a.75.75%200%200%201-.75-.75v-.006ZM13.498%2012.743a.75.75%200%200%201%20.75-.75h2.25a.75.75%200%201%201%200%201.5h-2.25a.75.75%200%200%201-.75-.75ZM6.748%2014.993a.75.75%200%200%201%20.75-.75h4.5a.75.75%200%200%201%200%201.5h-4.5a.75.75%200%200%201-.75-.75Z%22%20%2F%3E%0A%20%20%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M18%202.993a.75.75%200%200%200-1.5%200v1.5h-9V2.994a.75.75%200%201%200-1.5%200v1.497h-.752a3%203%200%200%200-3%203v11.252a3%203%200%200%200%203%203h13.5a3%203%200%200%200%203-3V7.492a3%203%200%200%200-3-3H18V2.993ZM3.748%2018.743v-7.5a1.5%201.5%200%200%201%201.5-1.5h13.5a1.5%201.5%200%200%201%201.5%201.5v7.5a1.5%201.5%200%200%201-1.5%201.5h-13.5a1.5%201.5%200%200%201-1.5-1.5Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%0A%3C%2Fsvg%3E");
217
+ }
218
+
219
+ .ti-icon.schedule {
220
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%3E%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%206v6h4.5m4.5%200a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Z%22%20%2F%3E%3C%2Fsvg%3E");
221
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20fill%3D%22none%22%20viewBox%3D%220%200%2024%2024%22%20stroke-width%3D%221.5%22%20stroke%3D%22currentColor%22%3E%3Cpath%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20d%3D%22M12%206v6h4.5m4.5%200a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Z%22%20%2F%3E%3C%2Fsvg%3E");
222
+ }
223
+
224
+ .ti-icon.schedule:hover {
225
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M12%202.25c-5.385%200-9.75%204.365-9.75%209.75s4.365%209.75%209.75%209.75%209.75-4.365%209.75-9.75S17.385%202.25%2012%202.25ZM12.75%206a.75.75%200%200%200-1.5%200v6c0%20.414.336.75.75.75h4.5a.75.75%200%200%200%200-1.5h-3.75V6Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%3C%2Fsvg%3E");
226
+ mask-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20d%3D%22M12%202.25c-5.385%200-9.75%204.365-9.75%209.75s4.365%209.75%209.75%209.75%209.75-4.365%209.75-9.75S17.385%202.25%2012%202.25ZM12.75%206a.75.75%200%200%200-1.5%200v6c0%20.414.336.75.75.75h4.5a.75.75%200%200%200%200-1.5h-3.75V6Z%22%20clip-rule%3D%22evenodd%22%20%2F%3E%3C%2Fsvg%3E");
227
+ }
228
+
229
+ /* ============================================================
230
+ Inline icons — small lucide-style stroked glyphs extracted
231
+ from the screens. All inherit colour via currentColor and
232
+ size via the .xs/.sm/.md/.lg/.xl modifiers on .ti-icon.
233
+ Group A: actions (plus, close, check, send, search)
234
+ Group B: status / signals (clock, warning-triangle, info,
235
+ check-circle, bell)
236
+ Group C: nouns (calendar-blank, user, users, briefcase,
237
+ book, folder, help-circle)
238
+ Group D: layout / navigation (chevron-left, chevron-right,
239
+ dashboard-grid, cycles-loop, sun, theme-toggle)
240
+ ============================================================ */
241
+
242
+ .ti-icon.plus {
243
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
244
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
245
+ }
246
+
247
+ .ti-icon.close {
248
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6L6 18M6 6l12 12'/%3E%3C/svg%3E");
249
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6L6 18M6 6l12 12'/%3E%3C/svg%3E");
250
+ }
251
+
252
+ .ti-icon.check {
253
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
254
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
255
+ }
256
+
257
+ /* Check inside a clipboard — used as the "evaluation" affordance on rows / panels. */
258
+ .ti-icon.check-clipboard {
259
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 11l3 3L22 4'/%3E%3Cpath d='M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11'/%3E%3C/svg%3E");
260
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 11l3 3L22 4'/%3E%3Cpath d='M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11'/%3E%3C/svg%3E");
261
+ }
262
+
263
+ /* Right-pointing solid triangle — open / submit / play. */
264
+ .ti-icon.send {
265
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor' stroke='currentColor' stroke-width='1' stroke-linejoin='round'%3E%3Cpolygon points='5 3 19 12 5 21 5 3'/%3E%3C/svg%3E");
266
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor' stroke='currentColor' stroke-width='1' stroke-linejoin='round'%3E%3Cpolygon points='5 3 19 12 5 21 5 3'/%3E%3C/svg%3E");
267
+ }
268
+
269
+ .ti-icon.search {
270
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
271
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
272
+ }
273
+
274
+ .ti-icon.clock {
275
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E");
276
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpolyline points='12 6 12 12 16 14'/%3E%3C/svg%3E");
277
+ }
278
+
279
+ .ti-icon.warning-triangle {
280
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'/%3E%3Cline x1='12' y1='9' x2='12' y2='13'/%3E%3Cline x1='12' y1='17' x2='12.01' y2='17'/%3E%3C/svg%3E");
281
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z'/%3E%3Cline x1='12' y1='9' x2='12' y2='13'/%3E%3Cline x1='12' y1='17' x2='12.01' y2='17'/%3E%3C/svg%3E");
282
+ }
283
+
284
+ .ti-icon.info-circle {
285
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='16' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12.01' y2='8'/%3E%3C/svg%3E");
286
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' y1='16' x2='12' y2='12'/%3E%3Cline x1='12' y1='8' x2='12.01' y2='8'/%3E%3C/svg%3E");
287
+ }
288
+
289
+ .ti-icon.bell {
290
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9'/%3E%3Cpath d='M13.73 21a2 2 0 0 1-3.46 0'/%3E%3C/svg%3E");
291
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9'/%3E%3Cpath d='M13.73 21a2 2 0 0 1-3.46 0'/%3E%3C/svg%3E");
292
+ }
293
+
294
+ /* Simple Feather-style calendar — used by deadline cards / period columns / date inputs. */
295
+ .ti-icon.calendar-blank {
296
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
297
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2'/%3E%3Cline x1='16' y1='2' x2='16' y2='6'/%3E%3Cline x1='8' y1='2' x2='8' y2='6'/%3E%3Cline x1='3' y1='10' x2='21' y2='10'/%3E%3C/svg%3E");
298
+ }
299
+
300
+ .ti-icon.user {
301
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E");
302
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='12' cy='7' r='4'/%3E%3C/svg%3E");
303
+ }
304
+
305
+ .ti-icon.users {
306
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='9' cy='7' r='4'/%3E%3Cpath d='M23 21v-2a4 4 0 0 0-3-3.87'/%3E%3Cpath d='M16 3.13a4 4 0 0 1 0 7.75'/%3E%3C/svg%3E");
307
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2'/%3E%3Ccircle cx='9' cy='7' r='4'/%3E%3Cpath d='M23 21v-2a4 4 0 0 0-3-3.87'/%3E%3Cpath d='M16 3.13a4 4 0 0 1 0 7.75'/%3E%3C/svg%3E");
308
+ }
309
+
310
+ .ti-icon.briefcase {
311
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='7' width='20' height='14' rx='2'/%3E%3Cpath d='M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2'/%3E%3C/svg%3E");
312
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='7' width='20' height='14' rx='2'/%3E%3Cpath d='M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2'/%3E%3C/svg%3E");
313
+ }
314
+
315
+ .ti-icon.folder {
316
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
317
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
318
+ }
319
+
320
+ .ti-icon.book {
321
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z'/%3E%3Cpath d='M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z'/%3E%3C/svg%3E");
322
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z'/%3E%3Cpath d='M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z'/%3E%3C/svg%3E");
323
+ }
324
+
325
+ .ti-icon.help-circle {
326
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3'/%3E%3Cline x1='12' y1='17' x2='12.01' y2='17'/%3E%3C/svg%3E");
327
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3'/%3E%3Cline x1='12' y1='17' x2='12.01' y2='17'/%3E%3C/svg%3E");
328
+ }
329
+
330
+ .ti-icon.chevron-left {
331
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='15 18 9 12 15 6'/%3E%3C/svg%3E");
332
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='15 18 9 12 15 6'/%3E%3C/svg%3E");
333
+ }
334
+
335
+ .ti-icon.chevron-right {
336
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'/%3E%3C/svg%3E");
337
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'/%3E%3C/svg%3E");
338
+ }
339
+
340
+ /* Four small rounded squares — sidebar Dashboard nav entry. */
341
+ .ti-icon.dashboard-grid {
342
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='3' y='14' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='14' width='7' height='7' rx='1.5'/%3E%3C/svg%3E");
343
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='3' width='7' height='7' rx='1.5'/%3E%3Crect x='3' y='14' width='7' height='7' rx='1.5'/%3E%3Crect x='14' y='14' width='7' height='7' rx='1.5'/%3E%3C/svg%3E");
344
+ }
345
+
346
+ /* Circular arrow with a small arrowhead — sidebar Cycles nav entry. */
347
+ .ti-icon.cycles-loop {
348
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-3-6.7'/%3E%3Cpolyline points='21 4 21 10 15 10'/%3E%3C/svg%3E");
349
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-3-6.7'/%3E%3Cpolyline points='21 4 21 10 15 10'/%3E%3C/svg%3E");
350
+ }
351
+
352
+ .ti-icon.sun {
353
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='5'/%3E%3Cline x1='12' y1='1' x2='12' y2='3'/%3E%3Cline x1='12' y1='21' x2='12' y2='23'/%3E%3Cline x1='4.22' y1='4.22' x2='5.64' y2='5.64'/%3E%3Cline x1='18.36' y1='18.36' x2='19.78' y2='19.78'/%3E%3Cline x1='1' y1='12' x2='3' y2='12'/%3E%3Cline x1='21' y1='12' x2='23' y2='12'/%3E%3Cline x1='4.22' y1='19.78' x2='5.64' y2='18.36'/%3E%3Cline x1='18.36' y1='5.64' x2='19.78' y2='4.22'/%3E%3C/svg%3E");
354
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='5'/%3E%3Cline x1='12' y1='1' x2='12' y2='3'/%3E%3Cline x1='12' y1='21' x2='12' y2='23'/%3E%3Cline x1='4.22' y1='4.22' x2='5.64' y2='5.64'/%3E%3Cline x1='18.36' y1='18.36' x2='19.78' y2='19.78'/%3E%3Cline x1='1' y1='12' x2='3' y2='12'/%3E%3Cline x1='21' y1='12' x2='23' y2='12'/%3E%3Cline x1='4.22' y1='19.78' x2='5.64' y2='18.36'/%3E%3Cline x1='18.36' y1='5.64' x2='19.78' y2='4.22'/%3E%3C/svg%3E");
355
+ }
356
+
357
+ .ti-icon.moon {
358
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'/%3E%3C/svg%3E");
359
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'/%3E%3C/svg%3E");
360
+ }
361
+
362
+ .ti-icon.eye {
363
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E");
364
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'/%3E%3Ccircle cx='12' cy='12' r='3'/%3E%3C/svg%3E");
365
+ }
366
+
367
+ /* Check inside a circle — Ready / completed indicator. */
368
+ .ti-icon.check-circle {
369
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 11.08V12a10 10 0 1 1-5.93-9.14'/%3E%3Cpolyline points='22 4 12 14.01 9 11.01'/%3E%3C/svg%3E");
370
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M22 11.08V12a10 10 0 1 1-5.93-9.14'/%3E%3Cpolyline points='22 4 12 14.01 9 11.01'/%3E%3C/svg%3E");
371
+ }
372
+
373
+ /* Three vertical bars of varying height — column / bar chart. */
374
+ .ti-icon.bar-chart {
375
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='20' x2='18' y2='10'/%3E%3Cline x1='12' y1='20' x2='12' y2='4'/%3E%3Cline x1='6' y1='20' x2='6' y2='14'/%3E%3C/svg%3E");
376
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='20' x2='18' y2='10'/%3E%3Cline x1='12' y1='20' x2='12' y2='4'/%3E%3Cline x1='6' y1='20' x2='6' y2='14'/%3E%3C/svg%3E");
377
+ }
378
+
379
+ .ti-main {
380
+ display: flex;
381
+ flex-direction: column;
382
+ width: 100%;
383
+ min-height: 100vh;
384
+ overflow: hidden;
385
+ }
386
+
387
+ .ti-sidebar {
388
+ display: flex;
389
+ flex-flow: column;
390
+ justify-content: space-between;
391
+ flex: auto;
392
+ z-index: 1000;
393
+ }
394
+
395
+
396
+ .ti-sidebar-button {
397
+ width: var(--ti-button-size);
398
+ height: var(--ti-button-size);
399
+ border-radius: var(--ti-button-radius);
400
+ display: inline-flex;
401
+ align-items: center;
402
+ justify-content: center;
403
+ cursor: pointer;
404
+ text-decoration: none;
405
+ }
406
+
407
+ .ti-sidebar-button:link {
408
+ text-decoration: none !important;
409
+ }
410
+
411
+ .ti-sidebar-button:visited {
412
+ text-decoration: none !important;
413
+ }
414
+
415
+ .ti-content {
416
+ display: flex;
417
+ align-items: center;
418
+ justify-content: center;
419
+ flex: auto;
420
+ width: 100%;
421
+ height: 100%;
422
+ min-height: 0;
423
+ }
424
+
425
+ .ti-content.pane {
426
+ display: flex;
427
+ flex-direction: column;
428
+ justify-content: flex-start;
429
+ gap: var(--ti-gap);
430
+ width: 100%;
431
+ height: 100%;
432
+ box-sizing: border-box;
433
+ padding: var(--ti-padding);
434
+ overflow: auto;
435
+ }
436
+
437
+
438
+ .ti-notifications {
439
+ position: fixed;
440
+ top: calc(env(safe-area-inset-top, 0px) + var(--ti-notification-top));
441
+ left: 0;
442
+ right: 0;
443
+ box-sizing: border-box;
444
+ padding-left: env(safe-area-inset-left, 0px);
445
+ padding-right: env(safe-area-inset-right, 0px);
446
+ display: flex;
447
+ align-items: center;
448
+ justify-content: center;
449
+ flex: auto;
450
+ width: auto;
451
+ z-index: 1100;
452
+ pointer-events: none;
453
+ }
454
+
455
+ .ti-notification {
456
+ display: inline-flex;
457
+ align-items: center;
458
+ justify-content: center;
459
+ flex-direction: row;
460
+ gap: var(--ti-gap);
461
+ cursor: pointer;
462
+ pointer-events: auto;
463
+ padding: var(--ti-padding);
464
+ border-radius: 12px;
465
+ max-width: 60%;
466
+ width: fit-content; /* Ensures the container shrinks to fit ti-content if text is short */
467
+ margin-left: auto; /* Centers the container if it has relative positioning */
468
+ margin-right: auto;
469
+ }
470
+
471
+ .ti-notification:focus-visible {
472
+ outline: 2px solid currentColor;
473
+ outline-offset: 2px;
474
+ }
475
+
476
+ /* Flyout (popover) menu styling */
477
+ .ti-sidebar-flyout-container {
478
+ display: inline-flex;
479
+ position: relative;
480
+ width: 100%;
481
+ }
482
+
483
+ .ti-sidebar-flyout[hidden] {
484
+ display: none !important;
485
+ }
486
+
487
+ .ti-sidebar-flyout {
488
+ /* Positioning */
489
+ position: fixed;
490
+ top: 0;
491
+ left: 0;
492
+ z-index: 1001;
493
+ transform: none;
494
+
495
+ /* Box model */
496
+ min-width: 220px;
497
+ padding: var(--s-2);
498
+ margin-left: 20px;
499
+ background: var(--bg-surface);
500
+ border: 1px solid var(--border);
501
+ border-radius: var(--r-lg);
502
+ box-shadow: var(--shadow-lg);
503
+
504
+ /* Layout */
505
+ display: flex;
506
+ flex-direction: column;
507
+ gap: 2px;
508
+ }
509
+
510
+ .ti-application.collapsed .ti-sidebar-flyout {
511
+ margin-left: 16px;
512
+ }
513
+
514
+ /* Flyout items */
515
+ .ti-sidebar-flyout-item {
516
+ display: inline-flex;
517
+ align-items: center;
518
+ justify-content: flex-start;
519
+ cursor: pointer;
520
+ gap: var(--s-3);
521
+ font-family: inherit;
522
+ font-size: var(--fs-base);
523
+ font-weight: 500;
524
+ -webkit-tap-highlight-color: transparent;
525
+ appearance: none;
526
+ width: 100%;
527
+ box-sizing: border-box;
528
+ border: 0;
529
+ background: transparent;
530
+ color: var(--fg-primary);
531
+ text-align: left;
532
+ padding: var(--s-2) var(--s-3);
533
+ border-radius: var(--r-md);
534
+ transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
535
+ }
536
+
537
+ .ti-sidebar-flyout-item .ti-icon {
538
+ width: 18px;
539
+ height: 18px;
540
+ background-color: var(--fg-secondary);
541
+ flex-shrink: 0;
542
+ }
543
+
544
+ .ti-sidebar-flyout-item:hover {
545
+ background: var(--bg-tint);
546
+ }
547
+
548
+ .ti-sidebar-flyout-item:hover .ti-icon {
549
+ background-color: var(--fg-primary);
550
+ }
551
+
552
+ .ti-sidebar-flyout-item:active {
553
+ background: var(--bg-sunken);
554
+ }
555
+
556
+ .ti-sidebar-flyout-item:focus-visible {
557
+ outline: none;
558
+ box-shadow: var(--shadow-focus);
559
+ }
560
+
561
+
562
+ .ti-tooltip {
563
+ position: relative;
564
+ display: inline-block;
565
+ }
566
+
567
+ .ti-tooltip-bubble {
568
+ position: absolute;
569
+ bottom: 100%;
570
+ left: 50%;
571
+ transform: translateX(-50%);
572
+ margin-bottom: var(--ti-tooltip-gap, 8px);
573
+ padding: var(--ti-tooltip-padding, 6px 12px);
574
+ background-color: var(--ti-tooltip-bg-color, rgba(0, 0, 0, 0.85));
575
+ color: var(--ti-tooltip-fg-color, white);
576
+ font-size: var(--ti-tooltip-font-size, 0.875rem);
577
+ line-height: var(--ti-tooltip-line-height, 1.4);
578
+ border-radius: var(--ti-tooltip-border-radius, 4px);
579
+ width: max-content;
580
+ max-width: var(--ti-tooltip-max-width, min(80vw, 320px));
581
+ white-space: normal;
582
+ overflow-wrap: break-word;
583
+ box-shadow: var(--ti-tooltip-shadow, none);
584
+ z-index: 1005;
585
+ pointer-events: none;
586
+ }
587
+
588
+ .ti-tooltip-bubble::before {
589
+ content: "";
590
+ position: absolute;
591
+ top: 100%;
592
+ left: 50%;
593
+ transform: translateX(-50%);
594
+ border: var(--ti-tooltip-arrow-size, 5px) solid transparent;
595
+ border-top-color: var(--ti-tooltip-bg-color, rgba(0, 0, 0, 0.85));
596
+ }
597
+
598
+ /* Mobile devices (tablets and phones) */
599
+ @media screen and (width <= 720px) {
600
+ .ti-notification {
601
+ max-width: 90%; /* Allows more space on smaller screens */
602
+ width: 90%; /* Ensures consistent width on very small screens */
603
+ }
604
+ }
605
+
606
+ /* ============================================================
607
+ Design System v2 — Shared tokens (theme-agnostic)
608
+ Theme-specific values live in ti-theme-daylight.css and
609
+ ti-theme-black-glass.css, keyed by [data-theme] attribute.
610
+ ============================================================ */
611
+
612
+ :root {
613
+ /* Type scale */
614
+ --fs-xxs: 10px;
615
+ --fs-xs: 11px;
616
+ --fs-sm: 12px;
617
+ --fs-base: 13.5px;
618
+ --fs-md: 15px;
619
+ --fs-lg: 18px;
620
+ --fs-xl: 22px;
621
+ --fs-2xl: 28px;
622
+ --fs-3xl: 36px;
623
+ --fs-display: 48px;
624
+
625
+ /* Spacing */
626
+ --s-1: 4px;
627
+ --s-2: 8px;
628
+ --s-3: 12px;
629
+ --s-4: 16px;
630
+ --s-5: 20px;
631
+ --s-6: 24px;
632
+ --s-7: 32px;
633
+ --s-8: 40px;
634
+ --s-9: 56px;
635
+ --s-10: 72px;
636
+
637
+ /* Radius */
638
+ --r-xs: 4px;
639
+ --r-sm: 6px;
640
+ --r-md: 10px;
641
+ --r-lg: 14px;
642
+ --r-xl: 20px;
643
+ --r-pill: 999px;
644
+
645
+ /* Layout */
646
+ --sidebar-w: 220px;
647
+ --sidebar-w-collapsed: 64px;
648
+ --header-h: 64px;
649
+ --content-max: 1180px;
650
+
651
+ /* Animation */
652
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
653
+ --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
654
+ --dur-fast: 140ms;
655
+ --dur-base: 220ms;
656
+ --dur-slow: 380ms;
657
+ }
658
+
659
+ /* ============================================================
660
+ App shell — CSS grid with collapsible ti-sidebar
661
+ ============================================================ */
662
+ .ti-application {
663
+ display: grid;
664
+ grid-template-columns: var(--sidebar-w) 1fr;
665
+ height: 100vh;
666
+ background: var(--bg-app);
667
+ transition: grid-template-columns var(--dur-base) var(--ease-out);
668
+ }
669
+
670
+ .ti-application.collapsed {
671
+ grid-template-columns: var(--sidebar-w-collapsed) 1fr;
672
+ }
673
+
674
+ /* ============================================================
675
+ Sidebar
676
+ ============================================================ */
677
+ .ti-sidebar {
678
+ background: var(--sidebar-bg);
679
+ color: var(--sidebar-fg);
680
+ display: flex;
681
+ flex-direction: column;
682
+ padding: var(--s-3) 0 var(--s-3);
683
+ position: sticky;
684
+ top: 0;
685
+ height: 100vh;
686
+ border-right: 1px solid var(--sidebar-divider);
687
+ overflow: hidden;
688
+ }
689
+
690
+ .ti-sidebar-collapse-btn {
691
+ position: absolute;
692
+ top: 20px;
693
+ right: -12px;
694
+ width: 22px;
695
+ height: 22px;
696
+ border-radius: 50%;
697
+ border: 1px solid var(--border);
698
+ background: var(--bg-surface);
699
+ color: var(--fg-secondary);
700
+ display: grid;
701
+ place-items: center;
702
+ cursor: pointer;
703
+ z-index: 11;
704
+ box-shadow: var(--shadow-sm);
705
+ opacity: 0;
706
+ transform: translateX(-4px);
707
+ transition: opacity var(--dur-fast), transform var(--dur-fast), background var(--dur-fast);
708
+ }
709
+
710
+ .ti-application:hover .ti-sidebar-collapse-btn,
711
+ .ti-sidebar-collapse-btn:focus-visible {
712
+ opacity: 1;
713
+ transform: translateX(0);
714
+ }
715
+
716
+ .ti-sidebar-collapse-btn:hover {
717
+ background: var(--accent);
718
+ color: #fff;
719
+ border-color: var(--accent);
720
+ }
721
+
722
+ .ti-application.collapsed .ti-sidebar-collapse-btn svg {
723
+ transform: rotate(180deg);
724
+ }
725
+
726
+ .ti-sidebar-collapse-btn svg {
727
+ transition: transform var(--dur-base) var(--ease-out);
728
+ }
729
+
730
+ .ti-sidebar-brand {
731
+ display: flex;
732
+ align-items: center;
733
+ gap: var(--s-3);
734
+ padding: var(--s-3) var(--s-4) var(--s-4);
735
+ color: var(--sidebar-fg-active);
736
+ white-space: nowrap;
737
+ min-height: 56px;
738
+ }
739
+
740
+ .ti-application.collapsed .ti-sidebar-brand {
741
+ padding-left: 16px;
742
+ padding-right: 16px;
743
+ }
744
+
745
+ .ti-application.collapsed .ti-sidebar-brand-text-block {
746
+ opacity: 0;
747
+ pointer-events: none;
748
+ }
749
+
750
+ .ti-sidebar-brand-text-block {
751
+ display: flex;
752
+ flex-direction: column;
753
+ min-width: 0;
754
+ transition: opacity var(--dur-fast);
755
+ }
756
+
757
+ .ti-sidebar-brand-mark {
758
+ width: 30px;
759
+ height: 30px;
760
+ display: grid;
761
+ place-items: center;
762
+ border-radius: 8px;
763
+ background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 70%, #000));
764
+ color: #FFF;
765
+ font-weight: 700;
766
+ font-size: 14px;
767
+ letter-spacing: -0.02em;
768
+ box-shadow: 0 4px 12px color-mix(in oklab, var(--accent) 32%, transparent), inset 0 1px 0 rgba(255, 255, 255, 0.25);
769
+ flex: 0 0 auto;
770
+ }
771
+
772
+ .ti-sidebar-brand-text {
773
+ font-weight: 600;
774
+ font-size: 15px;
775
+ letter-spacing: -0.01em;
776
+ }
777
+
778
+ .ti-sidebar-brand-sub {
779
+ font-size: 11px;
780
+ color: var(--sidebar-fg);
781
+ font-weight: 400;
782
+ }
783
+
784
+ .ti-sidebar-section-label {
785
+ font-size: 10px;
786
+ text-transform: uppercase;
787
+ letter-spacing: 0.1em;
788
+ color: var(--sidebar-fg);
789
+ opacity: 0.5;
790
+ padding: var(--s-2) var(--s-4) 6px;
791
+ margin-top: var(--s-3);
792
+ font-weight: 600;
793
+ white-space: nowrap;
794
+ overflow: hidden;
795
+ transition: opacity var(--dur-fast), padding var(--dur-fast);
796
+ }
797
+
798
+ .ti-application.collapsed .ti-sidebar-section-label {
799
+ font-size: 0;
800
+ padding-top: var(--s-3);
801
+ padding-bottom: 0;
802
+ margin-top: 0;
803
+ height: 8px;
804
+ position: relative;
805
+ }
806
+
807
+ .ti-application.collapsed .ti-sidebar-section-label::after {
808
+ content: "";
809
+ position: absolute;
810
+ left: 18px;
811
+ right: 18px;
812
+ top: 50%;
813
+ height: 1px;
814
+ background: var(--sidebar-divider);
815
+ }
816
+
817
+ .ti-sidebar-nav {
818
+ display: flex;
819
+ flex-direction: column;
820
+ gap: 2px;
821
+ padding: 0 var(--s-3);
822
+ }
823
+
824
+ .ti-sidebar-item {
825
+ position: relative;
826
+ display: flex;
827
+ align-items: center;
828
+ gap: var(--s-3);
829
+ padding: 8px 10px;
830
+ border-radius: 8px;
831
+ color: var(--sidebar-fg);
832
+ background: transparent;
833
+ border: 0;
834
+ text-align: left;
835
+ font-size: var(--fs-base);
836
+ font-family: inherit;
837
+ font-weight: 500;
838
+ cursor: pointer;
839
+ transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
840
+ width: 100%;
841
+ white-space: nowrap;
842
+ min-height: 36px;
843
+ text-decoration: none;
844
+ }
845
+
846
+ .ti-sidebar-item-label {
847
+ flex: 1;
848
+ overflow: hidden;
849
+ text-overflow: ellipsis;
850
+ transition: opacity var(--dur-fast);
851
+ }
852
+
853
+ .ti-application.collapsed .ti-sidebar-item-label {
854
+ opacity: 0;
855
+ pointer-events: none;
856
+ flex: 0 0 0;
857
+ max-width: 0;
858
+ margin: 0;
859
+ overflow: hidden;
860
+ }
861
+
862
+ .ti-application.collapsed .ti-sidebar-nav {
863
+ padding-left: var(--s-2);
864
+ padding-right: var(--s-2);
865
+ }
866
+
867
+ .ti-application.collapsed .ti-sidebar-foot {
868
+ padding-left: var(--s-2);
869
+ padding-right: var(--s-2);
870
+ }
871
+
872
+ .ti-application.collapsed .ti-sidebar-item {
873
+ justify-content: center;
874
+ padding-left: 0;
875
+ padding-right: 0;
876
+ gap: 0;
877
+ }
878
+
879
+ .ti-application.collapsed .ti-sidebar-item .ti-sidebar-item-icon {
880
+ margin: 0;
881
+ }
882
+
883
+ /* Collapsed tooltip */
884
+ .ti-application.collapsed .ti-sidebar-item:hover::after,
885
+ .ti-application.collapsed .ti-sidebar-user:hover::after {
886
+ content: attr(data-tip);
887
+ position: absolute;
888
+ left: calc(100% + 12px);
889
+ top: 50%;
890
+ transform: translateY(-50%);
891
+ background: var(--fg-primary);
892
+ color: var(--bg-surface);
893
+ font-size: 12px;
894
+ font-weight: 500;
895
+ padding: 5px 10px;
896
+ border-radius: 6px;
897
+ white-space: nowrap;
898
+ z-index: 50;
899
+ pointer-events: none;
900
+ box-shadow: var(--shadow-md);
901
+ }
902
+
903
+ .ti-sidebar-item:hover {
904
+ background: var(--sidebar-hover-bg);
905
+ color: var(--sidebar-fg-active);
906
+ }
907
+
908
+ .ti-sidebar-item.active {
909
+ background: var(--sidebar-active-bg);
910
+ color: var(--sidebar-fg-active);
911
+ }
912
+
913
+ .ti-sidebar-item.active::before {
914
+ content: "";
915
+ position: absolute;
916
+ left: calc(-1 * var(--s-3));
917
+ top: 8px;
918
+ bottom: 8px;
919
+ width: 3px;
920
+ border-radius: 3px;
921
+ background: var(--sidebar-accent);
922
+ }
923
+
924
+ .ti-application.collapsed .ti-sidebar-item.active::before {
925
+ left: calc(-1 * var(--s-2));
926
+ }
927
+
928
+ .ti-sidebar-item:disabled {
929
+ opacity: 0.5;
930
+ cursor: not-allowed;
931
+ pointer-events: none;
932
+ }
933
+
934
+ .ti-sidebar-item-icon {
935
+ width: 18px;
936
+ height: 18px;
937
+ flex: 0 0 auto;
938
+ display: grid;
939
+ place-items: center;
940
+ opacity: 0.85;
941
+ }
942
+
943
+ .ti-sidebar-item.active .ti-sidebar-item-icon {
944
+ opacity: 1;
945
+ }
946
+
947
+
948
+ .ti-sidebar-foot {
949
+ margin-top: auto;
950
+ padding: var(--s-2) var(--s-3) 0;
951
+ display: flex;
952
+ flex-direction: column;
953
+ gap: 2px;
954
+ border-top: 1px solid var(--sidebar-divider);
955
+ }
956
+
957
+ .ti-sidebar-user {
958
+ display: flex;
959
+ align-items: center;
960
+ gap: var(--s-3);
961
+ padding: 8px 10px;
962
+ border-radius: 10px;
963
+ cursor: pointer;
964
+ border: 0;
965
+ background: transparent;
966
+ color: var(--sidebar-fg);
967
+ text-align: left;
968
+ width: 100%;
969
+ white-space: nowrap;
970
+ min-height: 44px;
971
+ position: relative;
972
+ }
973
+
974
+ .ti-application.collapsed .ti-sidebar-user {
975
+ justify-content: center;
976
+ padding-left: 0;
977
+ padding-right: 0;
978
+ gap: 0;
979
+ }
980
+
981
+ .ti-application.collapsed .ti-sidebar-user-text {
982
+ display: none;
983
+ opacity: 0;
984
+ pointer-events: none;
985
+ }
986
+
987
+ .ti-sidebar-user-text {
988
+ display: flex;
989
+ flex-direction: column;
990
+ flex: 1;
991
+ min-width: 0;
992
+ transition: opacity var(--dur-fast);
993
+ }
994
+
995
+ .ti-sidebar-user:hover {
996
+ background: var(--sidebar-hover-bg);
997
+ }
998
+
999
+ .ti-sidebar-user-name {
1000
+ font-weight: 600;
1001
+ font-size: 13px;
1002
+ color: var(--sidebar-fg-active);
1003
+ overflow: hidden;
1004
+ text-overflow: ellipsis;
1005
+ }
1006
+
1007
+ /* Keep the role/department line to a single, ellipsised line — names like "Project & Delivery Management" would
1008
+ otherwise overflow the fixed-width sidebar. */
1009
+ .ti-sidebar-user-sub {
1010
+ font-size: 11px;
1011
+ opacity: 0.7;
1012
+ overflow: hidden;
1013
+ text-overflow: ellipsis;
1014
+ }
1015
+
1016
+ /* ============================================================
1017
+ Content area & ti-topbar
1018
+ ============================================================ */
1019
+ .ti-content {
1020
+ display: flex;
1021
+ flex-direction: column;
1022
+ min-width: 0;
1023
+ overflow: hidden;
1024
+ }
1025
+
1026
+ .ti-topbar {
1027
+ height: var(--header-h);
1028
+ width: 100%;
1029
+ display: flex;
1030
+ align-items: center;
1031
+ gap: var(--s-4);
1032
+ padding: 0 var(--s-7);
1033
+ border-bottom: 1px solid var(--border);
1034
+ background: var(--bg-surface);
1035
+ position: sticky;
1036
+ top: 0;
1037
+ z-index: 10;
1038
+ flex: 0 0 auto;
1039
+ }
1040
+
1041
+ .ti-topbar-title {
1042
+ font-size: var(--fs-lg);
1043
+ font-weight: 600;
1044
+ letter-spacing: -0.015em;
1045
+ color: var(--fg-primary);
1046
+ white-space: nowrap;
1047
+ }
1048
+
1049
+ .ti-topbar-sub {
1050
+ color: var(--fg-tertiary);
1051
+ font-size: var(--fs-sm);
1052
+ margin-left: var(--s-3);
1053
+ padding-left: var(--s-3);
1054
+ border-left: 1px solid var(--border);
1055
+ white-space: nowrap;
1056
+ }
1057
+
1058
+ #ti-topbar-info {
1059
+ display: flex;
1060
+ align-items: center;
1061
+ gap: var(--s-3);
1062
+ }
1063
+
1064
+ .ti-topbar-spacer {
1065
+ flex: 1;
1066
+ }
1067
+
1068
+ .ti-topbar-actions {
1069
+ display: flex;
1070
+ align-items: center;
1071
+ gap: var(--s-3);
1072
+ }
1073
+
1074
+ .ti-page {
1075
+ padding: var(--s-3) var(--s-5) var(--s-5);
1076
+ max-width: var(--content-max);
1077
+ margin: 0 auto;
1078
+ width: 100%;
1079
+ box-sizing: border-box;
1080
+ }
1081
+
1082
+
1083
+ .ti-page-scrollable {
1084
+ flex: 1;
1085
+ overflow-y: auto;
1086
+ min-height: 0;
1087
+ width: 100%;
1088
+ }
1089
+
1090
+ /* Hide OOB elements that land in the ti-content area before HTMX swaps them out */
1091
+ #ti-content [hx-swap-oob] {
1092
+ display: none;
1093
+ }
1094
+
1095
+ /* ============================================================
1096
+ Panel & ti-card
1097
+ ============================================================ */
1098
+ .ti-panel, .ti-card {
1099
+ background: var(--bg-surface);
1100
+ border: 1px solid var(--border);
1101
+ border-radius: var(--r-lg);
1102
+ box-shadow: var(--shadow-xs);
1103
+ overflow: hidden;
1104
+ }
1105
+
1106
+ /* Reusable card grid — a responsive two-column layout of .ti-card blocks; any direct child marked
1107
+ .ti-card-wide (or a non-card full-row element like a banner) spans the whole row. Collapses to one
1108
+ column on narrow viewports. App-agnostic; first consumed by the competence Insights screens. */
1109
+ .ti-card-grid {
1110
+ display: grid;
1111
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1112
+ gap: var(--s-5);
1113
+ align-items: start;
1114
+ }
1115
+
1116
+ .ti-card-grid > .ti-card-wide {
1117
+ grid-column: 1 / -1;
1118
+ }
1119
+
1120
+ @media ( max-width: 720px ) {
1121
+ .ti-card-grid {
1122
+ grid-template-columns: minmax(0, 1fr);
1123
+ }
1124
+ }
1125
+
1126
+ /* A muted, collapsible note/disclosure within a card (e.g. a "how it's calculated" methodology block). */
1127
+ .ti-card-note {
1128
+ margin-top: var(--s-3);
1129
+ font-size: var(--fs-sm);
1130
+ color: var(--fg-secondary);
1131
+ }
1132
+
1133
+ .ti-card-note > summary {
1134
+ cursor: pointer;
1135
+ color: var(--fg-secondary);
1136
+ list-style-position: inside;
1137
+ }
1138
+
1139
+ .ti-card-note > p {
1140
+ margin: var(--s-2) 0 0;
1141
+ line-height: 1.5;
1142
+ }
1143
+
1144
+ .ti-panel-head {
1145
+ display: flex;
1146
+ align-items: center;
1147
+ gap: var(--s-3);
1148
+ padding-bottom: var(--s-3);
1149
+ margin-bottom: var(--s-4);
1150
+ border-bottom: 1px solid var(--border-soft);
1151
+ }
1152
+
1153
+ /* Banner variant — the head reads as a full-width strip on a panel that does not use the default
1154
+ .ti-panel padding (e.g. sectioned cards with internal sub-content beneath the head). */
1155
+ .ti-panel-head.bar {
1156
+ padding: var(--s-4) var(--s-5);
1157
+ margin: 0;
1158
+ background: var(--bg-sunken);
1159
+ border-bottom: 1px solid var(--border-soft);
1160
+ }
1161
+
1162
+ /* Square icon on the left of the head. Sized for a 16-18px stroked SVG. */
1163
+ .ti-panel-head-icon {
1164
+ width: 32px;
1165
+ height: 32px;
1166
+ border-radius: var(--r-sm);
1167
+ background: var(--bg-surface);
1168
+ border: 1px solid var(--border);
1169
+ color: var(--fg-secondary);
1170
+ display: grid;
1171
+ place-items: center;
1172
+ flex: 0 0 auto;
1173
+ }
1174
+
1175
+ /* Wraps the title + subtitle so they stack while the head itself stays a single flex row. */
1176
+ .ti-panel-head-text {
1177
+ flex: 1;
1178
+ min-width: 0;
1179
+ display: flex;
1180
+ flex-direction: column;
1181
+ gap: 2px;
1182
+ }
1183
+
1184
+ .ti-panel-title {
1185
+ font-size: var(--fs-md);
1186
+ font-weight: 600;
1187
+ letter-spacing: -0.01em;
1188
+ color: var(--fg-primary);
1189
+ }
1190
+
1191
+ /* Small qualifier inline with the title (e.g. "(optional)"). */
1192
+ .ti-panel-title-aside {
1193
+ font-size: var(--fs-xs);
1194
+ font-weight: 500;
1195
+ color: var(--fg-tertiary);
1196
+ margin-left: var(--s-2);
1197
+ }
1198
+
1199
+ /* Smaller dimmed subtitle line under the title, optionally with an inline icon + meta string. */
1200
+ .ti-panel-subtitle {
1201
+ font-size: var(--fs-sm);
1202
+ color: var(--fg-tertiary);
1203
+ display: flex;
1204
+ align-items: center;
1205
+ gap: 6px;
1206
+ margin: 0;
1207
+ }
1208
+
1209
+ /* Right-aligned secondary info with a thin separator border on the left. Use for read-only
1210
+ informative content (counts, hints, recommended thresholds, etc.). */
1211
+ .ti-panel-head-aside {
1212
+ padding-left: var(--s-3);
1213
+ border-left: 1px solid var(--border);
1214
+ color: var(--fg-tertiary);
1215
+ font-size: var(--fs-sm);
1216
+ white-space: nowrap;
1217
+ flex: 0 0 auto;
1218
+ }
1219
+
1220
+ .ti-panel-head-aside strong {
1221
+ color: var(--fg-primary);
1222
+ font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Consolas, monospace;
1223
+ font-weight: 500;
1224
+ }
1225
+
1226
+ /* Right-aligned action buttons (Cancel / Save / + New …). Sits to the right alongside or in place
1227
+ of .ti-panel-head-aside. */
1228
+ .ti-panel-head-actions {
1229
+ margin-left: auto;
1230
+ display: flex;
1231
+ gap: var(--s-2);
1232
+ flex: 0 0 auto;
1233
+ }
1234
+
1235
+ /* When both .ti-panel-head-aside and .ti-panel-head-actions appear, only the aside gets the
1236
+ auto-margin — the actions sit immediately next to it. */
1237
+ .ti-panel-head-aside + .ti-panel-head-actions {
1238
+ margin-left: 0;
1239
+ }
1240
+
1241
+ .ti-panel-body-intro {
1242
+ font-size: var(--fs-sm);
1243
+ color: var(--fg-secondary);
1244
+ padding: 0 var(--s-3) var(--s-5);
1245
+ line-height: 1.5;
1246
+ }
1247
+
1248
+ /* ============================================================
1249
+ Buttons
1250
+ ============================================================ */
1251
+ .ti-btn {
1252
+ display: inline-flex;
1253
+ align-items: center;
1254
+ justify-content: center;
1255
+ gap: 7px;
1256
+ height: 36px;
1257
+ padding: 0 14px;
1258
+ border-radius: 9px;
1259
+ background: var(--bg-surface);
1260
+ border: 1px solid var(--border);
1261
+ color: var(--fg-primary);
1262
+ font-size: var(--fs-base);
1263
+ font-family: inherit;
1264
+ font-weight: 500;
1265
+ letter-spacing: -0.005em;
1266
+ cursor: pointer;
1267
+ white-space: nowrap;
1268
+ text-decoration: none;
1269
+ transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
1270
+ }
1271
+
1272
+ .ti-btn:hover {
1273
+ background: var(--bg-sunken);
1274
+ border-color: var(--border-strong);
1275
+ }
1276
+
1277
+ .ti-btn:active {
1278
+ transform: translateY(1px);
1279
+ }
1280
+
1281
+ .ti-btn:focus-visible {
1282
+ outline: none;
1283
+ box-shadow: var(--shadow-focus);
1284
+ }
1285
+
1286
+ .ti-btn[disabled] {
1287
+ opacity: 0.45;
1288
+ cursor: not-allowed;
1289
+ pointer-events: none;
1290
+ }
1291
+
1292
+ .ti-btn.primary {
1293
+ background: var(--accent);
1294
+ color: var(--fg-on-accent);
1295
+ border-color: transparent;
1296
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px color-mix(in oklab, var(--accent) 18%, transparent);
1297
+ }
1298
+
1299
+ .ti-btn.primary:hover {
1300
+ background: var(--accent-hover);
1301
+ }
1302
+
1303
+ .ti-btn.ghost {
1304
+ background: transparent;
1305
+ border-color: transparent;
1306
+ color: var(--fg-secondary);
1307
+ }
1308
+
1309
+ .ti-btn.ghost:hover {
1310
+ background: var(--bg-tint);
1311
+ color: var(--fg-primary);
1312
+ }
1313
+
1314
+ .ti-btn.danger {
1315
+ background: transparent;
1316
+ border-color: var(--border);
1317
+ color: var(--danger);
1318
+ }
1319
+
1320
+ .ti-btn.danger:hover {
1321
+ background: var(--danger-soft);
1322
+ border-color: var(--danger);
1323
+ }
1324
+
1325
+ .ti-btn.lg {
1326
+ height: 42px;
1327
+ padding: 0 18px;
1328
+ font-size: var(--fs-md);
1329
+ }
1330
+
1331
+ .ti-btn.sm {
1332
+ height: 28px;
1333
+ padding: 0 10px;
1334
+ font-size: var(--fs-sm);
1335
+ border-radius: 7px;
1336
+ }
1337
+
1338
+ .ti-btn.icon {
1339
+ width: 36px;
1340
+ padding: 0;
1341
+ }
1342
+
1343
+ .ti-btn.sm.icon {
1344
+ width: 28px;
1345
+ }
1346
+
1347
+
1348
+ /* ============================================================
1349
+ Inputs
1350
+ ============================================================ */
1351
+ .ti-input, .ti-select, .ti-textarea {
1352
+ width: 100%;
1353
+ height: 38px;
1354
+ padding: 0 12px;
1355
+ border-radius: 8px;
1356
+ background: var(--bg-surface);
1357
+ border: 1px solid var(--border);
1358
+ color: var(--fg-primary);
1359
+ font-size: var(--fs-base);
1360
+ font-family: inherit;
1361
+ outline: none;
1362
+ transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
1363
+ box-sizing: border-box;
1364
+ }
1365
+
1366
+ .ti-input:hover, .ti-select:hover, .ti-textarea:hover {
1367
+ border-color: var(--border-strong);
1368
+ }
1369
+
1370
+ .ti-input:focus, .ti-select:focus, .ti-textarea:focus {
1371
+ border-color: var(--accent);
1372
+ box-shadow: var(--shadow-focus);
1373
+ }
1374
+
1375
+ .ti-input.has-error, .ti-select.has-error, .ti-textarea.has-error {
1376
+ border-color: var(--danger);
1377
+ box-shadow: 0 0 0 3px var(--danger-soft);
1378
+ }
1379
+
1380
+ .ti-textarea {
1381
+ height: auto;
1382
+ padding: 10px 12px;
1383
+ resize: vertical;
1384
+ min-height: 100px;
1385
+ line-height: 1.5;
1386
+ }
1387
+
1388
+ /* Custom chevron for selects.
1389
+ The native <select> chevron position is browser-controlled and ends up visually offset from the calendar
1390
+ icon that <input type="date"> renders. Disable the native chevron with appearance:none and draw our own
1391
+ so both glyphs sit at the same right offset. Padding-right reserves the glyph slot; padding-right on
1392
+ the date input matches so the calendar icon's natural right margin lines up with the chevron.
1393
+ Theme-specific stroke colour because SVG inside background-image can't pick up currentColor. */
1394
+ .ti-select {
1395
+ appearance: none;
1396
+ -webkit-appearance: none;
1397
+ -moz-appearance: none;
1398
+ padding-right: 32px;
1399
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%238B8478' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
1400
+ background-repeat: no-repeat;
1401
+ background-position: right 10px center;
1402
+ background-size: 14px 14px;
1403
+ }
1404
+
1405
+ [data-theme="glass"] .ti-select {
1406
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23A6ACB8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
1407
+ }
1408
+
1409
+ /* Hide the legacy IE/Edge native arrow if the engine still renders it. */
1410
+ .ti-select::-ms-expand {
1411
+ display: none;
1412
+ }
1413
+
1414
+ /* Date input — leave the base padding alone (extra padding-right would shrink the content box and
1415
+ drag the calendar icon left of where it naturally sits, which is what we're trying to match). */
1416
+ .ti-input[type="date"]::-webkit-calendar-picker-indicator {
1417
+ cursor: pointer;
1418
+ opacity: 0.7;
1419
+ }
1420
+
1421
+ .ti-input[type="date"]::-webkit-calendar-picker-indicator:hover {
1422
+ opacity: 1;
1423
+ }
1424
+
1425
+ .ti-field-label {
1426
+ display: block;
1427
+ font-size: var(--fs-sm);
1428
+ font-weight: 500;
1429
+ color: var(--fg-secondary);
1430
+ margin-bottom: 6px;
1431
+ letter-spacing: -0.005em;
1432
+ }
1433
+
1434
+ /* ============================================================
1435
+ Status pill
1436
+ ============================================================ */
1437
+ .ti-status-pill {
1438
+ display: inline-flex;
1439
+ align-items: center;
1440
+ gap: 6px;
1441
+ height: 22px;
1442
+ padding: 0 9px;
1443
+ border-radius: var(--r-pill);
1444
+ font-size: var(--fs-sm);
1445
+ font-weight: 500;
1446
+ letter-spacing: -0.005em;
1447
+ background: var(--muted-soft);
1448
+ color: var(--fg-secondary);
1449
+ white-space: nowrap;
1450
+ }
1451
+
1452
+ .ti-status-pill .dot {
1453
+ width: 6px;
1454
+ height: 6px;
1455
+ border-radius: 50%;
1456
+ background: var(--muted);
1457
+ position: relative;
1458
+ }
1459
+
1460
+ .ti-status-pill.success {
1461
+ background: var(--success-soft);
1462
+ color: var(--success);
1463
+ }
1464
+
1465
+ .ti-status-pill.success .dot {
1466
+ background: var(--success);
1467
+ }
1468
+
1469
+ .ti-status-pill.danger .dot::after,
1470
+ .ti-status-pill.success .dot::after {
1471
+ content: "";
1472
+ position: absolute;
1473
+ inset: -3px;
1474
+ border-radius: 50%;
1475
+ color: var(--success);
1476
+ border: 2px solid var(--success);
1477
+ opacity: 0;
1478
+ animation: statusPulse 1.6s var(--ease-out) infinite;
1479
+ }
1480
+
1481
+ .ti-status-pill.info {
1482
+ background: var(--info-soft);
1483
+ color: var(--info);
1484
+ }
1485
+
1486
+ .ti-status-pill.info .dot {
1487
+ background: var(--info);
1488
+ }
1489
+
1490
+ .ti-status-pill.warn {
1491
+ background: var(--warn-soft);
1492
+ color: var(--warn);
1493
+ }
1494
+
1495
+ .ti-status-pill.warn .dot {
1496
+ background: var(--warn);
1497
+ }
1498
+
1499
+ .ti-status-pill.danger {
1500
+ background: var(--danger-soft);
1501
+ color: var(--danger);
1502
+ }
1503
+
1504
+ .ti-status-pill.danger .dot {
1505
+ background: var(--danger);
1506
+ }
1507
+
1508
+ .ti-status-pill.danger .dot::after {
1509
+ color: var(--danger);
1510
+ border: 2px solid var(--danger);
1511
+ }
1512
+
1513
+ .ti-status-pill.muted {
1514
+ background: var(--muted-soft);
1515
+ color: var(--fg-tertiary);
1516
+ }
1517
+
1518
+ .ti-status-pill.muted .dot {
1519
+ background: var(--fg-tertiary);
1520
+ }
1521
+
1522
+ @keyframes statusPulse {
1523
+ 0% {
1524
+ transform: scale(0.6);
1525
+ opacity: 0.5;
1526
+ }
1527
+ 100% {
1528
+ transform: scale(1.8);
1529
+ opacity: 0;
1530
+ }
1531
+ }
1532
+
1533
+
1534
+ /* ============================================================
1535
+ Grade chips
1536
+ ============================================================ */
1537
+ .ti-grade-chip {
1538
+ --c: var(--grade-N);
1539
+ --c-soft: var(--grade-N-soft);
1540
+ display: inline-flex;
1541
+ align-items: center;
1542
+ justify-content: center;
1543
+ width: 30px;
1544
+ height: 30px;
1545
+ border-radius: 8px;
1546
+ font-weight: 600;
1547
+ font-size: 13px;
1548
+ background: var(--c-soft);
1549
+ color: var(--c);
1550
+ border: 1px solid transparent;
1551
+ cursor: pointer;
1552
+ transition: transform var(--dur-fast) var(--ease-out), background var(--dur-fast), border-color var(--dur-fast), box-shadow var(--dur-fast);
1553
+ }
1554
+
1555
+ .ti-grade-chip[data-grade="S"] {
1556
+ --c: var(--grade-S);
1557
+ --c-soft: var(--grade-S-soft);
1558
+ }
1559
+
1560
+ .ti-grade-chip[data-grade="R"] {
1561
+ --c: var(--grade-R);
1562
+ --c-soft: var(--grade-R-soft);
1563
+ }
1564
+
1565
+ .ti-grade-chip[data-grade="U"] {
1566
+ --c: var(--grade-U);
1567
+ --c-soft: var(--grade-U-soft);
1568
+ }
1569
+
1570
+ .ti-grade-chip[data-grade="N"] {
1571
+ --c: var(--grade-N);
1572
+ --c-soft: var(--grade-N-soft);
1573
+ }
1574
+
1575
+ .ti-grade-chip.empty {
1576
+ background: transparent;
1577
+ color: var(--fg-tertiary);
1578
+ border: 1px dashed var(--border-strong);
1579
+ }
1580
+
1581
+ /* An empty grade chip means the rating is still awaited — render an hourglass glyph (lucide) rather than a bare
1582
+ dash, so the "awaiting rating" state reads clearly wherever an empty chip is shown to a permitted viewer. */
1583
+ .ti-grade-chip.empty::before {
1584
+ content: "";
1585
+ width: 12px;
1586
+ height: 12px;
1587
+ background-color: currentColor;
1588
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 22h14'/%3E%3Cpath d='M5 2h14'/%3E%3Cpath d='M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22'/%3E%3Cpath d='M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2'/%3E%3C/svg%3E") center / contain no-repeat;
1589
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 22h14'/%3E%3Cpath d='M5 2h14'/%3E%3Cpath d='M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22'/%3E%3Cpath d='M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2'/%3E%3C/svg%3E") center / contain no-repeat;
1590
+ }
1591
+
1592
+ .ti-grade-chip.selectable:hover {
1593
+ transform: translateY(-1px);
1594
+ border-color: var(--c);
1595
+ box-shadow: 0 4px 10px color-mix(in oklab, var(--c) 24%, transparent);
1596
+ }
1597
+
1598
+ .ti-grade-chip.selected {
1599
+ background: var(--c);
1600
+ color: var(--fg-on-accent);
1601
+ box-shadow: 0 4px 12px color-mix(in oklab, var(--c) 28%, transparent), inset 0 1px 0 rgba(255, 255, 255, 0.18);
1602
+ border-color: var(--c);
1603
+ }
1604
+
1605
+ .ti-grade-chip.locked {
1606
+ cursor: default;
1607
+ opacity: 0.65;
1608
+ }
1609
+
1610
+ .ti-grade-chip.locked.empty {
1611
+ opacity: 0.4;
1612
+ }
1613
+
1614
+
1615
+ /* ============================================================
1616
+ Avatar
1617
+ ============================================================ */
1618
+ .ti-avatar {
1619
+ display: inline-grid;
1620
+ place-items: center;
1621
+ border-radius: 50%;
1622
+ background: var(--avatar-bg, var(--accent));
1623
+ color: #FFF;
1624
+ font-weight: 600;
1625
+ font-size: 12px;
1626
+ flex: 0 0 auto;
1627
+ width: 30px;
1628
+ height: 30px;
1629
+ letter-spacing: 0.02em;
1630
+ text-transform: uppercase;
1631
+ }
1632
+
1633
+ .ti-avatar.lg {
1634
+ width: 44px;
1635
+ height: 44px;
1636
+ font-size: 15px;
1637
+ }
1638
+
1639
+ .ti-avatar.xl {
1640
+ width: 60px;
1641
+ height: 60px;
1642
+ font-size: 20px;
1643
+ }
1644
+
1645
+ .ti-avatar.sm {
1646
+ width: 24px;
1647
+ height: 24px;
1648
+ font-size: 10.5px;
1649
+ }
1650
+
1651
+ .ti-avatar.xs {
1652
+ width: 20px;
1653
+ height: 20px;
1654
+ font-size: 9px;
1655
+ }
1656
+
1657
+ /* ============================================================
1658
+ Toast stack (replaces old notification bar)
1659
+ ============================================================ */
1660
+ .ti-toast-stack {
1661
+ position: fixed;
1662
+ bottom: var(--s-5);
1663
+ left: 50%;
1664
+ transform: translateX(-50%);
1665
+ display: flex;
1666
+ flex-direction: column;
1667
+ gap: var(--s-3);
1668
+ /* Above the modal layer (backdrop is z-index 1200) so a toast raised while a modal is open stays visible. */
1669
+ z-index: 1300;
1670
+ pointer-events: none;
1671
+ }
1672
+
1673
+ .ti-toast {
1674
+ pointer-events: auto;
1675
+ display: flex;
1676
+ align-items: flex-start;
1677
+ gap: var(--s-3);
1678
+ padding: 12px 16px 12px 18px;
1679
+ background: var(--bg-surface);
1680
+ border: 1px solid var(--border);
1681
+ border-radius: var(--r-md);
1682
+ box-shadow: var(--shadow-lg);
1683
+ min-width: 320px;
1684
+ max-width: 440px;
1685
+ position: relative;
1686
+ animation: toastIn 360ms var(--ease-out);
1687
+ overflow: hidden;
1688
+ }
1689
+
1690
+ .ti-toast-bar {
1691
+ position: absolute;
1692
+ left: 0;
1693
+ top: 0;
1694
+ bottom: 0;
1695
+ width: 4px;
1696
+ background: var(--accent);
1697
+ }
1698
+
1699
+ .ti-toast.success .ti-toast-bar {
1700
+ background: var(--success);
1701
+ }
1702
+
1703
+ .ti-toast.warn .ti-toast-bar {
1704
+ background: var(--warn);
1705
+ }
1706
+
1707
+ .ti-toast.danger .ti-toast-bar {
1708
+ background: var(--danger);
1709
+ }
1710
+
1711
+ .ti-toast.info .ti-toast-bar {
1712
+ background: var(--info);
1713
+ }
1714
+
1715
+ .ti-toast.leaving {
1716
+ animation: toastOut 300ms var(--ease-in-out) forwards;
1717
+ }
1718
+
1719
+ .ti-toast-icon {
1720
+ width: 18px;
1721
+ height: 18px;
1722
+ flex: 0 0 auto;
1723
+ margin-top: 1px;
1724
+ display: grid;
1725
+ place-items: center;
1726
+ color: var(--fg-secondary);
1727
+ }
1728
+
1729
+ .ti-toast-icon svg {
1730
+ width: 18px;
1731
+ height: 18px;
1732
+ }
1733
+
1734
+ .ti-toast.success .ti-toast-icon {
1735
+ color: var(--success);
1736
+ }
1737
+
1738
+ .ti-toast.warn .ti-toast-icon {
1739
+ color: var(--warn);
1740
+ }
1741
+
1742
+ .ti-toast.danger .ti-toast-icon {
1743
+ color: var(--danger);
1744
+ }
1745
+
1746
+ .ti-toast.info .ti-toast-icon {
1747
+ color: var(--info);
1748
+ }
1749
+
1750
+ .ti-toast-body {
1751
+ flex: 1;
1752
+ min-width: 0;
1753
+ }
1754
+
1755
+ .ti-toast-title {
1756
+ font-weight: 600;
1757
+ font-size: var(--fs-base);
1758
+ color: var(--fg-primary);
1759
+ }
1760
+
1761
+ .ti-toast-desc,
1762
+ .ti-toast-msg {
1763
+ font-size: var(--fs-sm);
1764
+ color: var(--fg-secondary);
1765
+ margin-top: 2px;
1766
+ }
1767
+
1768
+ .ti-toast-details {
1769
+ font-size: var(--fs-xs);
1770
+ color: var(--fg-tertiary);
1771
+ margin-top: 3px;
1772
+ line-height: 1.4;
1773
+ overflow-wrap: anywhere;
1774
+ }
1775
+
1776
+ .ti-toast-close {
1777
+ flex: 0 0 auto;
1778
+ background: none;
1779
+ border: none;
1780
+ cursor: pointer;
1781
+ padding: 2px;
1782
+ color: var(--fg-tertiary);
1783
+ border-radius: 4px;
1784
+ line-height: 1;
1785
+ transition: color var(--dur-fast), background var(--dur-fast);
1786
+ }
1787
+
1788
+ .ti-toast-close:hover {
1789
+ color: var(--fg-primary);
1790
+ background: var(--bg-tint);
1791
+ }
1792
+
1793
+ @keyframes toastIn {
1794
+ from {
1795
+ opacity: 0;
1796
+ transform: translateY(12px) scale(0.96);
1797
+ }
1798
+ to {
1799
+ opacity: 1;
1800
+ transform: translateY(0) scale(1);
1801
+ }
1802
+ }
1803
+
1804
+ @keyframes toastOut {
1805
+ from {
1806
+ opacity: 1;
1807
+ transform: translateY(0) scale(1);
1808
+ }
1809
+ to {
1810
+ opacity: 0;
1811
+ transform: translateY(6px) scale(0.97);
1812
+ }
1813
+ }
1814
+
1815
+ /* ============================================================
1816
+ Modal / dialog overlay — shared web-framework primitive.
1817
+
1818
+ This is the canonical modal chrome for every ti-engine app. Use
1819
+ the following structure in any HTML fragment:
1820
+
1821
+ <template x-if="modal.kind === 'foo'">
1822
+ <div class="ti-modal-backdrop" @click.self="closeModal()" @keydown.escape.window="closeModal()">
1823
+ <div class="ti-modal medium" role="dialog" aria-modal="true">
1824
+ <div class="ti-modal-head">
1825
+ <div class="ti-modal-head-content">
1826
+ <span class="ti-modal-eyebrow">…</span>
1827
+ <div class="ti-modal-title">…</div>
1828
+ </div>
1829
+ <button class="ti-modal-close" @click="closeModal()" aria-label="Close">
1830
+ <span class="ti-icon close md" aria-hidden="true"></span>
1831
+ </button>
1832
+ </div>
1833
+ <div class="ti-modal-body">…</div>
1834
+ <div class="ti-modal-foot">…</div>
1835
+ </div>
1836
+ </div>
1837
+ </template>
1838
+
1839
+ Size variants: medium (640), large (880), full (~1100). Tone
1840
+ variants: danger (red head), warn (amber head). Footer alignment
1841
+ variants: between (space-between), start (flex-start).
1842
+
1843
+ App-specific styles (e.g. .competence-cycle-lock-target) live in
1844
+ the app's CSS and target content inside .ti-modal-body. Don't
1845
+ override .ti-modal-* in app CSS — extend with a new wrapper class.
1846
+ ============================================================ */
1847
+ .ti-modal-backdrop {
1848
+ position: fixed;
1849
+ inset: 0;
1850
+ background: rgba(0, 0, 0, 0.55);
1851
+ z-index: 1200;
1852
+ display: flex;
1853
+ align-items: center;
1854
+ justify-content: center;
1855
+ padding: var(--s-5);
1856
+ animation: modalBackdropIn var(--dur-base) var(--ease-out);
1857
+ }
1858
+
1859
+ [data-theme="glass"] .ti-modal-backdrop {
1860
+ background: rgba(0, 0, 0, 0.65);
1861
+ backdrop-filter: blur(6px);
1862
+ -webkit-backdrop-filter: blur(6px);
1863
+ }
1864
+
1865
+ .ti-modal {
1866
+ background: var(--bg-surface);
1867
+ border: 1px solid var(--border);
1868
+ border-radius: var(--r-lg);
1869
+ box-shadow: var(--shadow-lg);
1870
+ width: 100%;
1871
+ max-width: 480px;
1872
+ max-height: calc(100vh - var(--s-7));
1873
+ display: flex;
1874
+ flex-direction: column;
1875
+ overflow: hidden;
1876
+ animation: modalIn var(--dur-base) var(--ease-out);
1877
+ }
1878
+
1879
+ .ti-modal.medium {
1880
+ max-width: 640px;
1881
+ }
1882
+
1883
+ .ti-modal.large {
1884
+ max-width: 880px;
1885
+ }
1886
+
1887
+ .ti-modal.full {
1888
+ max-width: min(1100px, 100% - var(--s-7));
1889
+ }
1890
+
1891
+ .ti-modal-head-content {
1892
+ display: flex;
1893
+ flex-direction: column;
1894
+ flex-grow: 1;
1895
+ gap: var(--s-1);
1896
+ }
1897
+
1898
+ .ti-modal-head {
1899
+ display: flex;
1900
+ align-items: center;
1901
+ gap: var(--s-3);
1902
+ padding: var(--s-4) var(--s-5);
1903
+ border-bottom: 1px solid var(--border-soft);
1904
+ background: var(--bg-sunken);
1905
+ }
1906
+
1907
+ .ti-modal-title {
1908
+ flex: 1;
1909
+ font-size: var(--fs-md);
1910
+ font-weight: 600;
1911
+ color: var(--fg-primary);
1912
+ line-height: 1.3;
1913
+ }
1914
+
1915
+ .ti-modal-eyebrow {
1916
+ display: block;
1917
+ font-size: var(--fs-xs);
1918
+ font-weight: 500;
1919
+ text-transform: uppercase;
1920
+ letter-spacing: 0.05em;
1921
+ color: var(--fg-tertiary);
1922
+ padding: 0 var(--s-1);
1923
+ }
1924
+
1925
+ .ti-modal-close {
1926
+ flex: 0 0 auto;
1927
+ background: none;
1928
+ border: none;
1929
+ cursor: pointer;
1930
+ padding: 4px;
1931
+ color: var(--fg-tertiary);
1932
+ border-radius: var(--r-xs);
1933
+ line-height: 1;
1934
+ display: grid;
1935
+ place-items: center;
1936
+ transition: color var(--dur-fast), background var(--dur-fast);
1937
+ }
1938
+
1939
+ .ti-modal-close:hover {
1940
+ color: var(--fg-primary);
1941
+ background: var(--bg-tint);
1942
+ }
1943
+
1944
+ .ti-modal-close svg {
1945
+ width: 16px;
1946
+ height: 16px;
1947
+ }
1948
+
1949
+ .ti-modal-body {
1950
+ flex: 1 1 auto;
1951
+ overflow-y: auto;
1952
+ padding: var(--s-5);
1953
+ color: var(--fg-primary);
1954
+ font-size: var(--fs-base);
1955
+ line-height: 1.5;
1956
+ }
1957
+
1958
+ .ti-modal-body p {
1959
+ margin: 0 0 var(--s-3) 0;
1960
+ }
1961
+
1962
+ .ti-modal-body p:last-child {
1963
+ margin-bottom: 0;
1964
+ }
1965
+
1966
+ .ti-modal-foot {
1967
+ display: flex;
1968
+ align-items: center;
1969
+ justify-content: flex-end;
1970
+ gap: var(--s-3);
1971
+ padding: var(--s-3) var(--s-5);
1972
+ border-top: 1px solid var(--border-soft);
1973
+ background: var(--bg-sunken);
1974
+ }
1975
+
1976
+ .ti-modal-foot.between {
1977
+ justify-content: space-between;
1978
+ }
1979
+
1980
+ .ti-modal-foot.start {
1981
+ justify-content: flex-start;
1982
+ }
1983
+
1984
+ .ti-modal.danger .ti-modal-head {
1985
+ background: var(--danger-soft);
1986
+ }
1987
+
1988
+ .ti-modal.danger .ti-modal-title {
1989
+ color: var(--danger);
1990
+ }
1991
+
1992
+ .ti-modal.warn .ti-modal-head {
1993
+ background: var(--warn-soft);
1994
+ }
1995
+
1996
+ .ti-modal.warn .ti-modal-title {
1997
+ color: var(--warn);
1998
+ }
1999
+
2000
+ @keyframes modalBackdropIn {
2001
+ from {
2002
+ opacity: 0;
2003
+ }
2004
+ to {
2005
+ opacity: 1;
2006
+ }
2007
+ }
2008
+
2009
+ @keyframes modalIn {
2010
+ from {
2011
+ opacity: 0;
2012
+ transform: translateY(8px) scale(0.97);
2013
+ }
2014
+ to {
2015
+ opacity: 1;
2016
+ transform: translateY(0) scale(1);
2017
+ }
2018
+ }
2019
+
2020
+ /* ============================================================
2021
+ KV grid (label / value pairs)
2022
+ ============================================================ */
2023
+ .ti-kv-grid {
2024
+ display: grid;
2025
+ grid-template-columns: auto 1fr;
2026
+ gap: var(--s-2) var(--s-4);
2027
+ align-items: baseline;
2028
+ }
2029
+
2030
+ .ti-kv-label {
2031
+ font-size: var(--fs-xs);
2032
+ font-weight: 600;
2033
+ color: var(--fg-tertiary);
2034
+ text-transform: uppercase;
2035
+ letter-spacing: 0.05em;
2036
+ white-space: nowrap;
2037
+ }
2038
+
2039
+ .ti-kv-value {
2040
+ font-size: var(--fs-sm);
2041
+ font-weight: 400;
2042
+ color: var(--fg-primary);
2043
+ }
2044
+
2045
+ /* ============================================================
2046
+ Tag / pill label
2047
+ ============================================================ */
2048
+ .ti-tag {
2049
+ display: inline-flex;
2050
+ align-items: center;
2051
+ gap: 5px;
2052
+ height: 20px;
2053
+ padding: 0 8px;
2054
+ border-radius: var(--r-pill);
2055
+ font-size: var(--fs-xs);
2056
+ font-weight: 600;
2057
+ letter-spacing: 0.03em;
2058
+ text-transform: uppercase;
2059
+ background: var(--muted-soft);
2060
+ color: var(--fg-tertiary);
2061
+ white-space: nowrap;
2062
+ }
2063
+
2064
+ .ti-tag.mono {
2065
+ font-family: "JetBrains Mono", monospace;
2066
+ letter-spacing: 0;
2067
+ text-transform: none;
2068
+ }
2069
+
2070
+ /* ============================================================
2071
+ Empty state
2072
+ ============================================================ */
2073
+ .ti-empty-state {
2074
+ display: flex;
2075
+ flex-direction: column;
2076
+ align-items: center;
2077
+ justify-content: center;
2078
+ gap: var(--s-3);
2079
+ padding: var(--s-10) var(--s-6);
2080
+ text-align: center;
2081
+ }
2082
+
2083
+ .ti-empty-state-icon {
2084
+ width: 48px;
2085
+ height: 48px;
2086
+ opacity: 0.3;
2087
+ }
2088
+
2089
+ .ti-empty-state-title {
2090
+ font-size: var(--fs-md);
2091
+ font-weight: 600;
2092
+ color: var(--fg-primary);
2093
+ }
2094
+
2095
+ .ti-empty-state-desc {
2096
+ font-size: var(--fs-base);
2097
+ color: var(--fg-tertiary);
2098
+ max-width: 40ch;
2099
+ line-height: 1.55;
2100
+ }
2101
+
2102
+ /* ============================================================
2103
+ Page layout helpers — eyebrow / title / subtitle stacked
2104
+ vertically. Used as the single canonical page header pattern
2105
+ across every screen.
2106
+ ============================================================ */
2107
+ .ti-page-head {
2108
+ display: flex;
2109
+ flex-direction: column;
2110
+ margin-bottom: var(--s-5);
2111
+ gap: var(--s-1);
2112
+ }
2113
+
2114
+ .ti-page-eyebrow {
2115
+ font-size: var(--fs-xs);
2116
+ font-weight: 600;
2117
+ text-transform: uppercase;
2118
+ letter-spacing: 0.12em;
2119
+ color: var(--fg-tertiary);
2120
+ padding: 0 var(--s-1)
2121
+ }
2122
+
2123
+ .ti-page-title {
2124
+ font-size: var(--fs-2xl);
2125
+ font-weight: 600;
2126
+ letter-spacing: -0.025em;
2127
+ line-height: 1.15;
2128
+ color: var(--fg-primary);
2129
+ margin: 0;
2130
+ }
2131
+
2132
+ .ti-page-subtitle {
2133
+ color: var(--fg-tertiary);
2134
+ margin: 0;
2135
+ font-size: var(--fs-sm);
2136
+ line-height: 1.55;
2137
+ padding: var(--s-1);
2138
+ }
2139
+
2140
+ /* ============================================================
2141
+ Form layout helpers — single canonical pattern for modal /
2142
+ master-detail / panel forms. Compose grid + row + section
2143
+ instead of cloning per-screen CSS.
2144
+ ============================================================ */
2145
+ .ti-form {
2146
+ display: flex;
2147
+ flex-direction: column;
2148
+ gap: var(--s-5);
2149
+ }
2150
+
2151
+ .ti-form.compact {
2152
+ gap: var(--s-4);
2153
+ }
2154
+
2155
+ .ti-form-section {
2156
+ display: flex;
2157
+ flex-direction: column;
2158
+ gap: var(--s-3);
2159
+ }
2160
+
2161
+ .ti-form-section-title {
2162
+ font-size: var(--fs-xs);
2163
+ font-weight: 600;
2164
+ color: var(--fg-tertiary);
2165
+ text-transform: uppercase;
2166
+ letter-spacing: 0.05em;
2167
+ }
2168
+
2169
+ .ti-form-grid {
2170
+ display: grid;
2171
+ grid-template-columns: 1fr 1fr;
2172
+ gap: var(--s-3);
2173
+ }
2174
+
2175
+ .ti-form-grid.cols-1 {
2176
+ grid-template-columns: 1fr;
2177
+ }
2178
+
2179
+ .ti-form-grid.cols-3 {
2180
+ grid-template-columns: repeat(3, minmax(0, 1fr));
2181
+ }
2182
+
2183
+ .ti-form-row {
2184
+ display: flex;
2185
+ flex-direction: column;
2186
+ gap: var(--s-2);
2187
+ }
2188
+
2189
+ .ti-form-row.wide {
2190
+ grid-column: 1 / -1;
2191
+ }
2192
+
2193
+ .ti-form-readonly {
2194
+ padding: 6px 0;
2195
+ color: var(--fg-primary);
2196
+ font-size: var(--fs-base);
2197
+ display: flex;
2198
+ flex-direction: column;
2199
+ gap: 2px;
2200
+ }
2201
+
2202
+ .ti-form-hint {
2203
+ font-size: var(--fs-xs);
2204
+ color: var(--fg-tertiary);
2205
+ }
2206
+
2207
+ .ti-form-error {
2208
+ color: var(--danger);
2209
+ font-size: var(--fs-sm);
2210
+ background: var(--danger-soft);
2211
+ border: 1px solid var(--danger);
2212
+ padding: var(--s-2) var(--s-3);
2213
+ border-radius: var(--r-sm);
2214
+ }
2215
+
2216
+ .ti-form-actions {
2217
+ display: flex;
2218
+ align-items: center;
2219
+ gap: var(--s-3);
2220
+ padding-top: var(--s-3);
2221
+ border-top: 1px solid var(--border-soft);
2222
+ }
2223
+
2224
+ .ti-form-state {
2225
+ flex: 1;
2226
+ font-size: var(--fs-sm);
2227
+ }
2228
+
2229
+ .ti-form-state.saved {
2230
+ color: var(--success);
2231
+ }
2232
+
2233
+ .ti-form-state.unsaved {
2234
+ color: var(--warn);
2235
+ font-weight: 500;
2236
+ }
2237
+
2238
+ @media (max-width: 720px) {
2239
+ .ti-form-grid,
2240
+ .ti-form-grid.cols-3 {
2241
+ grid-template-columns: 1fr;
2242
+ }
2243
+ }
2244
+
2245
+ /* ============================================================
2246
+ Data grid — CSS-grid replacement for <table>. Pass column
2247
+ widths via --ti-grid-cols (set on the wrapper or on a parent
2248
+ class). Head + rows share the same template so cells align.
2249
+ Use .ti-cell-center / .ti-cell-right on any direct grid child
2250
+ to override alignment.
2251
+ ============================================================ */
2252
+ .ti-data-grid {
2253
+ width: 100%;
2254
+ /* Sensible single-column fallback. Override on a screen-specific class. */
2255
+ --ti-grid-cols: 1fr;
2256
+ }
2257
+
2258
+ .ti-data-grid-head {
2259
+ display: grid;
2260
+ grid-template-columns: var(--ti-grid-cols);
2261
+ gap: var(--s-3);
2262
+ padding: var(--s-2) var(--s-5);
2263
+ font-size: var(--fs-xxs);
2264
+ font-weight: 600;
2265
+ text-transform: uppercase;
2266
+ letter-spacing: 0.08em;
2267
+ color: var(--fg-tertiary);
2268
+ border-bottom: 1px solid var(--border-soft);
2269
+ white-space: nowrap;
2270
+ align-items: center;
2271
+ }
2272
+
2273
+ .ti-data-grid-rows {
2274
+ display: flex;
2275
+ flex-direction: column;
2276
+ }
2277
+
2278
+ .ti-data-grid-row {
2279
+ display: grid;
2280
+ grid-template-columns: var(--ti-grid-cols);
2281
+ gap: var(--s-3);
2282
+ align-items: center;
2283
+ padding: var(--s-3) var(--s-5);
2284
+ transition: background var(--dur-fast);
2285
+ color: var(--fg-primary);
2286
+ font-size: var(--fs-sm);
2287
+ }
2288
+
2289
+ .ti-data-grid-row:hover {
2290
+ background: var(--bg-tint);
2291
+ }
2292
+
2293
+ /* Highlight the row representing the current user / actor. Used by the org tree to mark
2294
+ "this is you". */
2295
+ .ti-data-grid-row.is-current {
2296
+ background: var(--accent-soft);
2297
+ }
2298
+
2299
+ .ti-data-grid-row.is-current:hover {
2300
+ background: color-mix(in oklab, var(--accent-soft) 80%, var(--accent) 6%);
2301
+ }
2302
+
2303
+ /* Highlight a user-picked row (e.g., selected cycle, selected employee). */
2304
+ .ti-data-grid-row.is-selected {
2305
+ background: var(--accent-soft);
2306
+ border-left: 2px solid var(--accent);
2307
+ padding-left: calc(var(--s-5) - 2px);
2308
+ }
2309
+
2310
+ /* Bordered variant — visible horizontal divider between rows. Use on tabular displays. */
2311
+ .ti-data-grid.bordered .ti-data-grid-row {
2312
+ border-bottom: 1px solid var(--border-soft);
2313
+ }
2314
+
2315
+ .ti-data-grid.bordered .ti-data-grid-row:last-child {
2316
+ border-bottom: 0;
2317
+ }
2318
+
2319
+ /* Compact variant — tighter row padding for dense data. */
2320
+ .ti-data-grid.compact .ti-data-grid-row,
2321
+ .ti-data-grid.compact .ti-data-grid-head {
2322
+ padding: var(--s-2) var(--s-4);
2323
+ }
2324
+
2325
+ /* Per-cell alignment utilities. Apply directly to any grid-child element
2326
+ (span / div / button) to override the default left alignment. */
2327
+ .ti-cell-center {
2328
+ justify-self: center;
2329
+ text-align: center;
2330
+ }
2331
+
2332
+ .ti-cell-right {
2333
+ justify-self: end;
2334
+ text-align: right;
2335
+ }
2336
+
2337
+ /* ============================================================
2338
+ Login ti-page
2339
+ ============================================================ */
2340
+ #ti-login {
2341
+ display: flex;
2342
+ align-items: center;
2343
+ justify-content: center;
2344
+ min-height: 100vh;
2345
+ width: 100%;
2346
+ background: var(--bg-app);
2347
+ }
2348
+
2349
+ .ti-login-container {
2350
+ width: 100%;
2351
+ max-width: 400px;
2352
+ padding: var(--s-5);
2353
+ }
2354
+
2355
+ .ti-login-brand {
2356
+ display: flex;
2357
+ flex-direction: column;
2358
+ align-items: center;
2359
+ gap: var(--s-3);
2360
+ margin-bottom: var(--s-7);
2361
+ }
2362
+
2363
+ .ti-login-brand-mark {
2364
+ width: 48px;
2365
+ height: 48px;
2366
+ border-radius: 14px;
2367
+ background: linear-gradient(135deg, var(--accent), color-mix(in oklab, var(--accent) 70%, #000));
2368
+ display: grid;
2369
+ place-items: center;
2370
+ color: #fff;
2371
+ font-size: 22px;
2372
+ font-weight: 700;
2373
+ letter-spacing: -0.02em;
2374
+ box-shadow: 0 8px 24px color-mix(in oklab, var(--accent) 28%, transparent),
2375
+ inset 0 1px 0 rgba(255, 255, 255, 0.25);
2376
+ }
2377
+
2378
+ .ti-login-brand-text {
2379
+ text-align: center;
2380
+ }
2381
+
2382
+ .ti-login-brand-title {
2383
+ font-size: var(--fs-xl);
2384
+ font-weight: 700;
2385
+ letter-spacing: -0.025em;
2386
+ color: var(--fg-primary);
2387
+ }
2388
+
2389
+ .ti-login-brand-sub {
2390
+ font-size: var(--fs-base);
2391
+ color: var(--fg-tertiary);
2392
+ margin-top: 4px;
2393
+ }
2394
+
2395
+ .ti-login-card {
2396
+ background: var(--bg-surface);
2397
+ border: 1px solid var(--border);
2398
+ border-radius: var(--r-xl);
2399
+ padding: var(--s-6);
2400
+ box-shadow: var(--shadow-lg);
2401
+ }
2402
+
2403
+ .ti-login-error {
2404
+ display: none;
2405
+ color: var(--danger);
2406
+ font-size: var(--fs-sm);
2407
+ margin-bottom: var(--s-4);
2408
+ padding: var(--s-3);
2409
+ background: var(--danger-soft);
2410
+ border-radius: var(--r-md);
2411
+ }
2412
+
2413
+ .ti-login-form {
2414
+ display: flex;
2415
+ flex-direction: column;
2416
+ gap: var(--s-4);
2417
+ }
2418
+
2419
+ .ti-login-submit {
2420
+ margin-top: var(--s-1);
2421
+ }
2422
+
2423
+ .ti-login-divider {
2424
+ display: flex;
2425
+ align-items: center;
2426
+ gap: var(--s-3);
2427
+ margin: var(--s-5) 0;
2428
+ }
2429
+
2430
+ .ti-login-divider-line {
2431
+ flex: 1;
2432
+ height: 1px;
2433
+ background: var(--border);
2434
+ }
2435
+
2436
+ .ti-login-divider-label {
2437
+ font-size: var(--fs-xs);
2438
+ color: var(--fg-tertiary);
2439
+ text-transform: uppercase;
2440
+ letter-spacing: 0.08em;
2441
+ font-weight: 600;
2442
+ }
2443
+
2444
+ .ti-login-social {
2445
+ display: flex;
2446
+ flex-direction: column;
2447
+ gap: var(--s-2);
2448
+ }
2449
+
2450
+ .ti-btn.full {
2451
+ width: 100%;
2452
+ }
2453
+
2454
+ /* TEMPORARY: Test user selector panel — remove with the corresponding panel in frame-login.html */
2455
+ .ti-login-test-panel {
2456
+ margin-top: var(--s-4);
2457
+ padding: var(--s-4);
2458
+ background: var(--bg-surface);
2459
+ border: 1px dashed var(--border-strong);
2460
+ border-radius: var(--r-lg);
2461
+ box-shadow: var(--shadow-xs);
2462
+ }
2463
+
2464
+ .ti-login-test-panel-title {
2465
+ font-size: var(--fs-sm);
2466
+ font-weight: 600;
2467
+ color: var(--fg-primary);
2468
+ text-transform: uppercase;
2469
+ letter-spacing: 0.06em;
2470
+ }
2471
+
2472
+ .ti-login-test-panel-sub {
2473
+ font-size: var(--fs-xs);
2474
+ color: var(--fg-tertiary);
2475
+ margin-top: 2px;
2476
+ margin-bottom: var(--s-3);
2477
+ }
2478
+
2479
+ .ti-login-test-panel-pills {
2480
+ display: flex;
2481
+ flex-wrap: wrap;
2482
+ gap: var(--s-2);
2483
+ }
2484
+
2485
+ .ti-login-test-pill {
2486
+ display: inline-flex;
2487
+ align-items: baseline;
2488
+ gap: var(--s-2);
2489
+ padding: 6px 12px;
2490
+ background: var(--bg-tint);
2491
+ border: 1px solid var(--border);
2492
+ border-radius: var(--r-pill);
2493
+ cursor: pointer;
2494
+ font-family: inherit;
2495
+ font-size: var(--fs-sm);
2496
+ color: var(--fg-secondary);
2497
+ transition: background var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
2498
+ }
2499
+
2500
+ .ti-login-test-pill:hover {
2501
+ background: var(--bg-sunken);
2502
+ border-color: var(--border-strong);
2503
+ color: var(--fg-primary);
2504
+ }
2505
+
2506
+ .ti-login-test-pill.selected {
2507
+ background: var(--accent);
2508
+ border-color: transparent;
2509
+ color: var(--fg-on-accent);
2510
+ box-shadow: 0 1px 2px color-mix(in oklab, var(--accent) 24%, transparent);
2511
+ }
2512
+
2513
+ .ti-login-test-pill.selected .ti-login-test-pill-roles {
2514
+ color: color-mix(in oklab, var(--fg-on-accent) 80%, transparent);
2515
+ }
2516
+
2517
+ .ti-login-test-pill-id {
2518
+ font-weight: 600;
2519
+ }
2520
+
2521
+ .ti-login-test-pill-roles {
2522
+ font-size: var(--fs-xs);
2523
+ color: var(--fg-tertiary);
2524
+ }
2525
+
2526
+ .ti-login-test-pill.ghost {
2527
+ background: transparent;
2528
+ border-color: transparent;
2529
+ color: var(--fg-tertiary);
2530
+ }
2531
+
2532
+ .ti-login-test-pill.ghost:hover {
2533
+ color: var(--fg-primary);
2534
+ background: var(--bg-tint);
2535
+ }
2536
+
2537
+ .ti-login-test-panel-override {
2538
+ display: flex;
2539
+ align-items: center;
2540
+ gap: var(--s-2);
2541
+ margin-top: var(--s-3);
2542
+ font-size: var(--fs-xs);
2543
+ color: var(--fg-tertiary);
2544
+ cursor: pointer;
2545
+ }
2546
+
2547
+ /* ============================================================
2548
+ Utility
2549
+ ============================================================ */
2550
+ * {
2551
+ box-sizing: border-box;
2552
+ }
2553
+
2554
+ /* Flexible spacer: pushes following siblings to the far end of a flex row/column. */
2555
+ .ti-spacer {
2556
+ flex: 1 1 auto;
2557
+ }
2558
+
2559
+ ::-webkit-scrollbar {
2560
+ width: 8px;
2561
+ height: 8px;
2562
+ }
2563
+
2564
+ ::-webkit-scrollbar-track {
2565
+ background: transparent;
2566
+ }
2567
+
2568
+ ::-webkit-scrollbar-thumb {
2569
+ background: var(--border, rgba(0, 0, 0, 0.15));
2570
+ border-radius: 999px;
2571
+ border: 2px solid transparent;
2572
+ background-clip: padding-box;
2573
+ }
2574
+
2575
+ ::-webkit-scrollbar-thumb:hover {
2576
+ background: var(--border-strong, rgba(0, 0, 0, 0.28));
2577
+ background-clip: padding-box;
2578
+ border: 2px solid transparent;
2579
+ }
2580
+
2581
+ /* ------------------------------------------------------------------ *
2582
+ * ti-charts — themeable SVG chart primitives (Phase 0: gauge/bars/stat)
2583
+ * Colors route through semantic + grade tokens that flip per theme in
2584
+ * ti-theme-daylight.css / ti-theme-black-glass.css. No per-chart overrides.
2585
+ * ------------------------------------------------------------------ */
2586
+ .ti-chart {
2587
+ --c-axis: var(--border-strong);
2588
+ --c-grid: var(--grid-line);
2589
+ --c-ink: var(--fg-secondary);
2590
+ --c-ink-strong: var(--fg-primary);
2591
+ --c-series-1: var(--accent);
2592
+ --c-series-2: var(--info);
2593
+ --c-series-3: var(--success);
2594
+ --c-grade-s: var(--grade-S);
2595
+ --c-grade-r: var(--grade-R);
2596
+ --c-grade-u: var(--grade-U);
2597
+ --c-grade-n: var(--grade-N);
2598
+ --c-seq-1: var(--chart-seq-1);
2599
+ --c-seq-2: var(--chart-seq-2);
2600
+ --c-seq-3: var(--chart-seq-3);
2601
+ --c-seq-4: var(--chart-seq-4);
2602
+ --c-seq-5: var(--chart-seq-5);
2603
+ display: block;
2604
+ margin: 0;
2605
+ position: relative;
2606
+ }
2607
+
2608
+ .ti-chart svg {
2609
+ display: block;
2610
+ width: 100%;
2611
+ height: auto;
2612
+ overflow: visible;
2613
+ /* Charts scale to 100% of their card; cap the height (and per-type width below) + centre so a chart on a wide card
2614
+ does not balloon to many hundreds of px tall. preserveAspectRatio="xMidYMid meet" keeps the drawing centred. */
2615
+ max-height: 460px;
2616
+ margin-inline: auto;
2617
+ }
2618
+
2619
+ /* Per-type size caps (the figure carries data-ti-chart-type, set by renderChart). Square KPIs stay small and centred;
2620
+ content charts use width but never overrun the height cap. */
2621
+ .ti-chart[data-ti-chart-type="gauge"] svg {
2622
+ max-width: 240px;
2623
+ }
2624
+
2625
+ .ti-chart[data-ti-chart-type="scatter"] svg {
2626
+ max-width: 380px;
2627
+ }
2628
+
2629
+ .ti-chart[data-ti-chart-type="heatmap"] svg {
2630
+ max-width: 480px;
2631
+ }
2632
+
2633
+ .ti-chart[data-ti-chart-type="box"] svg {
2634
+ max-width: 680px;
2635
+ }
2636
+
2637
+ /* Horizontal bars grow with their row count, so they opt out of the global max-height: with the cap, a tall org-wide
2638
+ chart was uniformly scaled down and ended up finer than the same chart on a smaller subtree. Without it the bar
2639
+ thickness and label size stay identical across screens; the chart simply gets taller when there are more rows. */
2640
+ .ti-chart[data-ti-chart-type="bars"] svg {
2641
+ max-width: 640px;
2642
+ max-height: none;
2643
+ }
2644
+
2645
+ .ti-chart[data-ti-chart-type="radar"] svg {
2646
+ max-width: 360px;
2647
+ }
2648
+
2649
+ .ti-chart[data-ti-chart-type="line"] svg {
2650
+ max-width: 640px;
2651
+ }
2652
+
2653
+ /* Visually-hidden a11y mirror table (screen-reader only). */
2654
+ .ti-chart-sr {
2655
+ position: absolute;
2656
+ width: 1px;
2657
+ height: 1px;
2658
+ padding: 0;
2659
+ margin: -1px;
2660
+ overflow: hidden;
2661
+ clip-path: inset(50%);
2662
+ white-space: nowrap;
2663
+ border: 0;
2664
+ }
2665
+
2666
+ /* Provisional ("as of now / % reporting") visual language — class form;
2667
+ renderers may instead set stroke-dasharray as a presentation attribute. */
2668
+ .ti-chart-provisional {
2669
+ stroke-dasharray: 4 3;
2670
+ opacity: 0.55;
2671
+ }
2672
+
2673
+ /* --- gauge --- */
2674
+ .ti-chart-gauge-track {
2675
+ stroke: var(--c-grid);
2676
+ stroke-width: 8;
2677
+ stroke-linecap: round;
2678
+ }
2679
+
2680
+ .ti-chart-gauge-arc {
2681
+ stroke: var(--c-series-1);
2682
+ stroke-width: 8;
2683
+ stroke-linecap: round;
2684
+ }
2685
+
2686
+ .ti-chart-gauge-value {
2687
+ fill: var(--c-ink-strong);
2688
+ font-size: 18px;
2689
+ font-weight: 600;
2690
+ font-variant-numeric: tabular-nums;
2691
+ }
2692
+
2693
+ .ti-chart-gauge-label {
2694
+ fill: var(--c-ink);
2695
+ font-size: 7px;
2696
+ }
2697
+
2698
+ .ti-chart-gauge-sublabel {
2699
+ fill: var(--c-ink);
2700
+ font-size: 5px;
2701
+ }
2702
+
2703
+ /* --- bars --- */
2704
+ .ti-chart-bar-seg {
2705
+ fill: var(--c-series-1);
2706
+ }
2707
+
2708
+ .ti-chart-bar-seg.tone-grade-s {
2709
+ fill: var(--c-grade-s);
2710
+ }
2711
+
2712
+ .ti-chart-bar-seg.tone-grade-r {
2713
+ fill: var(--c-grade-r);
2714
+ }
2715
+
2716
+ .ti-chart-bar-seg.tone-grade-u {
2717
+ fill: var(--c-grade-u);
2718
+ }
2719
+
2720
+ .ti-chart-bar-seg.tone-grade-n {
2721
+ fill: var(--c-grade-n);
2722
+ }
2723
+
2724
+ .ti-chart-bar-seg.tone-ink {
2725
+ fill: var(--c-grid);
2726
+ }
2727
+
2728
+ .ti-chart-bar-seg.tone-info {
2729
+ fill: var(--c-series-2);
2730
+ }
2731
+
2732
+ /* --- stat tile --- */
2733
+ .ti-chart-stat {
2734
+ display: flex;
2735
+ flex-direction: column;
2736
+ gap: 2px;
2737
+ }
2738
+
2739
+ .ti-chart-stat-value {
2740
+ color: var(--c-ink-strong);
2741
+ font-size: 1.6rem;
2742
+ font-weight: 600;
2743
+ line-height: 1.1;
2744
+ }
2745
+
2746
+ .ti-chart-stat-label {
2747
+ color: var(--c-ink-strong);
2748
+ font-size: 0.9rem;
2749
+ }
2750
+
2751
+ .ti-chart-stat-sub {
2752
+ color: var(--c-ink);
2753
+ font-size: 0.8rem;
2754
+ }
2755
+
2756
+ .ti-chart-stat-bar {
2757
+ height: 6px;
2758
+ border-radius: 999px;
2759
+ background: var(--c-grid);
2760
+ overflow: hidden;
2761
+ margin-top: 4px;
2762
+ }
2763
+
2764
+ .ti-chart-stat-fill {
2765
+ height: 100%;
2766
+ width: var(--pct, 0%);
2767
+ background: var(--c-series-1);
2768
+ border-radius: inherit;
2769
+ }
2770
+
2771
+ /* tabular-nums utility used by chart text (no such class existed before). */
2772
+ .tabular-nums {
2773
+ font-variant-numeric: tabular-nums;
2774
+ }
2775
+
2776
+ /* ------------------------------------------------------------------ *
2777
+ * ti-charts — Phase 1A primitives (scatter, heatmap, box) + bar modes.
2778
+ * Same token discipline: colors route through --c-* (set on .ti-chart).
2779
+ * ------------------------------------------------------------------ */
2780
+
2781
+ /* Interactive marks (drill) — CSP-safe: tabindex/role set in JS, no inline handlers. */
2782
+ .ti-chart [role="button"] {
2783
+ cursor: pointer;
2784
+ }
2785
+
2786
+ .ti-chart [role="button"]:focus-visible {
2787
+ outline: 1px solid var(--c-series-1);
2788
+ outline-offset: 1px;
2789
+ }
2790
+
2791
+ /* --- scatter (R3 alignment quadrant) --- */
2792
+ .ti-chart-scatter-diag {
2793
+ stroke: var(--c-grid);
2794
+ stroke-width: 0.5;
2795
+ stroke-dasharray: 2 2;
2796
+ }
2797
+
2798
+ .ti-chart-scatter-mid {
2799
+ stroke: var(--c-grid);
2800
+ stroke-width: 0.4;
2801
+ }
2802
+
2803
+ .ti-chart-scatter-pt {
2804
+ fill: var(--c-series-1);
2805
+ stroke: var(--c-ink-strong);
2806
+ stroke-width: 0.3;
2807
+ }
2808
+
2809
+ .ti-chart-scatter-pt.tone-grade-s {
2810
+ fill: var(--c-grade-s);
2811
+ }
2812
+
2813
+ .ti-chart-scatter-pt.tone-grade-r {
2814
+ fill: var(--c-grade-r);
2815
+ }
2816
+
2817
+ .ti-chart-scatter-pt.tone-grade-u {
2818
+ fill: var(--c-grade-u);
2819
+ }
2820
+
2821
+ .ti-chart-scatter-pt.tone-grade-n {
2822
+ fill: var(--c-grade-n);
2823
+ }
2824
+
2825
+ .ti-chart-scatter-pt.tone-info {
2826
+ fill: var(--c-series-2);
2827
+ }
2828
+
2829
+ .ti-chart-scatter-quad {
2830
+ fill: var(--c-series-1);
2831
+ opacity: 0.05;
2832
+ }
2833
+
2834
+ .ti-chart-axis-label {
2835
+ fill: var(--c-ink);
2836
+ font-size: 4px;
2837
+ }
2838
+
2839
+ /* --- heatmap (R4) — sequential ramp + diverging grade scale --- */
2840
+ .ti-chart-heat-cell {
2841
+ stroke: var(--bg-elevated, #fff);
2842
+ stroke-width: 0.5;
2843
+ }
2844
+
2845
+ .ti-chart-heat-cell.cell-q1 {
2846
+ fill: var(--c-seq-1);
2847
+ }
2848
+
2849
+ .ti-chart-heat-cell.cell-q2 {
2850
+ fill: var(--c-seq-2);
2851
+ }
2852
+
2853
+ .ti-chart-heat-cell.cell-q3 {
2854
+ fill: var(--c-seq-3);
2855
+ }
2856
+
2857
+ .ti-chart-heat-cell.cell-q4 {
2858
+ fill: var(--c-seq-4);
2859
+ }
2860
+
2861
+ .ti-chart-heat-cell.cell-q5 {
2862
+ fill: var(--c-seq-5);
2863
+ }
2864
+
2865
+ .ti-chart-heat-cell.cell-pos {
2866
+ fill: var(--c-grade-s);
2867
+ }
2868
+
2869
+ /* diverging: positive gap → S side */
2870
+ .ti-chart-heat-cell.cell-neg {
2871
+ fill: var(--c-grade-n);
2872
+ }
2873
+
2874
+ /* diverging: negative gap → N side */
2875
+ .ti-chart-heat-cell.cell-zero {
2876
+ fill: var(--c-grid);
2877
+ }
2878
+
2879
+ .ti-chart-heat-cell.suppressed {
2880
+ fill: var(--c-grid);
2881
+ opacity: 0.4;
2882
+ }
2883
+
2884
+ .ti-chart-heat-label {
2885
+ fill: var(--c-ink);
2886
+ font-size: 4px;
2887
+ }
2888
+
2889
+ /* --- box (R5 level correlation) --- */
2890
+ .ti-chart-box-box {
2891
+ fill: var(--c-series-1);
2892
+ opacity: 0.22;
2893
+ stroke: var(--c-series-1);
2894
+ stroke-width: 0.6;
2895
+ }
2896
+
2897
+ .ti-chart-box-median {
2898
+ stroke: var(--c-ink-strong);
2899
+ stroke-width: 1;
2900
+ }
2901
+
2902
+ .ti-chart-box-whisker {
2903
+ stroke: var(--c-axis);
2904
+ stroke-width: 0.5;
2905
+ }
2906
+
2907
+ .ti-chart-box-cap {
2908
+ stroke: var(--c-axis);
2909
+ stroke-width: 0.5;
2910
+ }
2911
+
2912
+ .ti-chart-box-mean {
2913
+ fill: var(--c-ink-strong);
2914
+ }
2915
+
2916
+ .ti-chart-box-expected {
2917
+ stroke: var(--c-grade-r);
2918
+ stroke-width: 0.8;
2919
+ stroke-dasharray: 2 2;
2920
+ }
2921
+
2922
+ .ti-chart-box-ref {
2923
+ stroke: var(--c-grade-u);
2924
+ stroke-width: 0.5;
2925
+ stroke-dasharray: 3 2;
2926
+ }
2927
+
2928
+ .ti-chart-box-ref-label {
2929
+ fill: var(--c-grade-u);
2930
+ font-size: 4px;
2931
+ }
2932
+
2933
+ .ti-chart-box-label {
2934
+ fill: var(--c-ink);
2935
+ font-size: 4px;
2936
+ }
2937
+
2938
+ /* --- bars (grouped + diverging modes) --- */
2939
+ .ti-chart-bar-axis {
2940
+ stroke: var(--c-axis);
2941
+ stroke-width: 0.4;
2942
+ }
2943
+
2944
+ .ti-chart-bar-label {
2945
+ fill: var(--c-ink);
2946
+ font-size: 4px;
2947
+ }
2948
+
2949
+ /* Grouped-bar value caption. Deliberately sets NO font-size so the renderer's font-size presentation attribute
2950
+ (default 4, or spec.options.valueFontSize) is honoured — a CSS font-size here would override that attribute. */
2951
+ .ti-chart-bar-value {
2952
+ fill: var(--c-ink);
2953
+ }
2954
+
2955
+ /* Chart legend — a visible swatch+label key appended below a chart (e.g. the coverage stacked bars). Inherits the
2956
+ --c-grade-* / --c-ink tokens from the parent .ti-chart, so swatches match the segment fills and re-theme with them. */
2957
+ .ti-chart-legend {
2958
+ display: flex;
2959
+ flex-wrap: wrap;
2960
+ justify-content: center;
2961
+ gap: var(--s-2) var(--s-4);
2962
+ margin-top: var(--s-3);
2963
+ font-size: var(--fs-sm);
2964
+ color: var(--fg-secondary);
2965
+ }
2966
+
2967
+ .ti-chart-legend-item {
2968
+ display: inline-flex;
2969
+ align-items: center;
2970
+ gap: 6px;
2971
+ }
2972
+
2973
+ .ti-chart-legend-swatch {
2974
+ width: 12px;
2975
+ height: 12px;
2976
+ border-radius: 3px;
2977
+ background: var(--c-ink);
2978
+ flex: 0 0 auto;
2979
+ }
2980
+
2981
+ .ti-chart-legend-swatch.tone-grade-s {
2982
+ background: var(--c-grade-s);
2983
+ }
2984
+
2985
+ .ti-chart-legend-swatch.tone-grade-r {
2986
+ background: var(--c-grade-r);
2987
+ }
2988
+
2989
+ .ti-chart-legend-swatch.tone-grade-u {
2990
+ background: var(--c-grade-u);
2991
+ }
2992
+
2993
+ .ti-chart-legend-swatch.tone-grade-n {
2994
+ background: var(--c-grade-n);
2995
+ }
2996
+
2997
+ .ti-chart-legend-swatch.tone-ink {
2998
+ background: var(--c-ink);
2999
+ }
3000
+
3001
+ .ti-chart-legend-swatch.tone-info {
3002
+ background: var(--c-series-2);
3003
+ }
3004
+
3005
+ .ti-chart-legend-swatch.is-dashed {
3006
+ background: transparent;
3007
+ border: 1px dashed var(--c-ink);
3008
+ }
3009
+
3010
+ /* --- radar (individual subcategory profile) --- */
3011
+ .ti-chart-radar-ring {
3012
+ stroke: var(--c-grid);
3013
+ stroke-width: 0.4;
3014
+ }
3015
+
3016
+ .ti-chart-radar-spoke {
3017
+ stroke: var(--c-grid);
3018
+ stroke-width: 0.3;
3019
+ }
3020
+
3021
+ .ti-chart-radar-axis-label {
3022
+ fill: var(--c-ink);
3023
+ font-size: 3.6px;
3024
+ }
3025
+
3026
+ .ti-chart-radar-poly {
3027
+ fill: var(--c-series-1);
3028
+ fill-opacity: 0.12;
3029
+ stroke: var(--c-series-1);
3030
+ stroke-width: 0.8;
3031
+ }
3032
+
3033
+ .ti-chart-radar-poly.tone-grade-s {
3034
+ fill: var(--c-grade-s);
3035
+ stroke: var(--c-grade-s);
3036
+ }
3037
+
3038
+ .ti-chart-radar-poly.tone-grade-r {
3039
+ fill: var(--c-grade-r);
3040
+ stroke: var(--c-grade-r);
3041
+ }
3042
+
3043
+ .ti-chart-radar-poly.tone-grade-u {
3044
+ fill: var(--c-grade-u);
3045
+ stroke: var(--c-grade-u);
3046
+ }
3047
+
3048
+ .ti-chart-radar-poly.tone-grade-n {
3049
+ fill: var(--c-grade-n);
3050
+ stroke: var(--c-grade-n);
3051
+ }
3052
+
3053
+ .ti-chart-radar-poly.tone-info {
3054
+ fill: var(--c-series-2);
3055
+ stroke: var(--c-series-2);
3056
+ }
3057
+
3058
+ .ti-chart-radar-dot {
3059
+ fill: var(--c-series-1);
3060
+ }
3061
+
3062
+ .ti-chart-radar-dot.tone-grade-s {
3063
+ fill: var(--c-grade-s);
3064
+ }
3065
+
3066
+ .ti-chart-radar-dot.tone-grade-r {
3067
+ fill: var(--c-grade-r);
3068
+ }
3069
+
3070
+ .ti-chart-radar-dot.tone-grade-u {
3071
+ fill: var(--c-grade-u);
3072
+ }
3073
+
3074
+ .ti-chart-radar-dot.tone-grade-n {
3075
+ fill: var(--c-grade-n);
3076
+ }
3077
+
3078
+ .ti-chart-radar-dot.tone-info {
3079
+ fill: var(--c-series-2);
3080
+ }
3081
+
3082
+ /* --- line / trend (cross-cycle, CA-X1) --- */
3083
+ .ti-chart-line-axis {
3084
+ stroke: var(--c-grid);
3085
+ stroke-width: 0.3;
3086
+ }
3087
+
3088
+ .ti-chart-line-xlabel {
3089
+ fill: var(--c-ink);
3090
+ font-size: 3.2px;
3091
+ }
3092
+
3093
+ .ti-chart-line-series {
3094
+ stroke: var(--c-series-1);
3095
+ stroke-width: 1;
3096
+ fill: none;
3097
+ stroke-linejoin: round;
3098
+ stroke-linecap: round;
3099
+ }
3100
+
3101
+ .ti-chart-line-series.tone-grade-s {
3102
+ stroke: var(--c-grade-s);
3103
+ }
3104
+
3105
+ .ti-chart-line-series.tone-grade-r {
3106
+ stroke: var(--c-grade-r);
3107
+ }
3108
+
3109
+ .ti-chart-line-series.tone-grade-u {
3110
+ stroke: var(--c-grade-u);
3111
+ }
3112
+
3113
+ .ti-chart-line-series.tone-grade-n {
3114
+ stroke: var(--c-grade-n);
3115
+ }
3116
+
3117
+ .ti-chart-line-series.tone-info {
3118
+ stroke: var(--c-series-2);
3119
+ }
3120
+
3121
+ .ti-chart-line-band {
3122
+ fill: var(--c-series-1);
3123
+ fill-opacity: 0.12;
3124
+ stroke: none;
3125
+ }
3126
+
3127
+ .ti-chart-line-band.tone-grade-s {
3128
+ fill: var(--c-grade-s);
3129
+ }
3130
+
3131
+ .ti-chart-line-band.tone-grade-r {
3132
+ fill: var(--c-grade-r);
3133
+ }
3134
+
3135
+ .ti-chart-line-band.tone-grade-u {
3136
+ fill: var(--c-grade-u);
3137
+ }
3138
+
3139
+ .ti-chart-line-band.tone-grade-n {
3140
+ fill: var(--c-grade-n);
3141
+ }
3142
+
3143
+ .ti-chart-line-band.tone-info {
3144
+ fill: var(--c-series-2);
3145
+ }
3146
+
3147
+ .ti-chart-line-dot {
3148
+ fill: var(--c-series-1);
3149
+ }
3150
+
3151
+ .ti-chart-line-dot.tone-grade-s {
3152
+ fill: var(--c-grade-s);
3153
+ }
3154
+
3155
+ .ti-chart-line-dot.tone-grade-r {
3156
+ fill: var(--c-grade-r);
3157
+ }
3158
+
3159
+ .ti-chart-line-dot.tone-grade-u {
3160
+ fill: var(--c-grade-u);
3161
+ }
3162
+
3163
+ .ti-chart-line-dot.tone-grade-n {
3164
+ fill: var(--c-grade-n);
3165
+ }
3166
+
3167
+ .ti-chart-line-dot.tone-info {
3168
+ fill: var(--c-series-2);
3169
+ }
3170
+
3171
+ .ti-chart-line-dot.provisional {
3172
+ fill: var(--bg-app);
3173
+ stroke: var(--c-series-1);
3174
+ stroke-width: 0.6;
3175
+ }
3176
+
3177
+ .ti-chart-line-dot.tone-grade-s.provisional {
3178
+ stroke: var(--c-grade-s);
3179
+ }
3180
+
3181
+ .ti-chart-line-dot.tone-grade-r.provisional {
3182
+ stroke: var(--c-grade-r);
3183
+ }
3184
+
3185
+ .ti-chart-line-dot.tone-grade-u.provisional {
3186
+ stroke: var(--c-grade-u);
3187
+ }
3188
+
3189
+ .ti-chart-line-dot.tone-grade-n.provisional {
3190
+ stroke: var(--c-grade-n);
3191
+ }
3192
+
3193
+ .ti-chart-line-dot.tone-info.provisional {
3194
+ stroke: var(--c-series-2);
3195
+ }