tamga-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/blocks/count-row.d.ts +29 -0
  4. package/dist/blocks/count-row.d.ts.map +1 -0
  5. package/dist/blocks/count-row.js +19 -0
  6. package/dist/blocks/filter-bar.d.ts +123 -0
  7. package/dist/blocks/filter-bar.d.ts.map +1 -0
  8. package/dist/blocks/filter-bar.js +74 -0
  9. package/dist/blocks/index.d.ts +24 -0
  10. package/dist/blocks/index.d.ts.map +1 -0
  11. package/dist/blocks/index.js +23 -0
  12. package/dist/blocks/save-bar.d.ts +31 -0
  13. package/dist/blocks/save-bar.d.ts.map +1 -0
  14. package/dist/blocks/save-bar.js +21 -0
  15. package/dist/components/a11y.d.ts +21 -0
  16. package/dist/components/a11y.d.ts.map +1 -0
  17. package/dist/components/a11y.js +159 -0
  18. package/dist/components/advanced-input.d.ts +115 -0
  19. package/dist/components/advanced-input.d.ts.map +1 -0
  20. package/dist/components/advanced-input.js +189 -0
  21. package/dist/components/avatar.d.ts +63 -0
  22. package/dist/components/avatar.d.ts.map +1 -0
  23. package/dist/components/avatar.js +54 -0
  24. package/dist/components/badge.d.ts +37 -0
  25. package/dist/components/badge.d.ts.map +1 -0
  26. package/dist/components/badge.js +29 -0
  27. package/dist/components/bar.d.ts +32 -0
  28. package/dist/components/bar.d.ts.map +1 -0
  29. package/dist/components/bar.js +33 -0
  30. package/dist/components/button.d.ts +45 -0
  31. package/dist/components/button.d.ts.map +1 -0
  32. package/dist/components/button.js +56 -0
  33. package/dist/components/chart.d.ts +43 -0
  34. package/dist/components/chart.d.ts.map +1 -0
  35. package/dist/components/chart.js +41 -0
  36. package/dist/components/checkbox.d.ts +13 -0
  37. package/dist/components/checkbox.d.ts.map +1 -0
  38. package/dist/components/checkbox.js +16 -0
  39. package/dist/components/chrome.d.ts +87 -0
  40. package/dist/components/chrome.d.ts.map +1 -0
  41. package/dist/components/chrome.js +107 -0
  42. package/dist/components/combobox.d.ts +31 -0
  43. package/dist/components/combobox.d.ts.map +1 -0
  44. package/dist/components/combobox.js +62 -0
  45. package/dist/components/control-base.d.ts +7 -0
  46. package/dist/components/control-base.d.ts.map +1 -0
  47. package/dist/components/control-base.js +6 -0
  48. package/dist/components/data-table.d.ts +68 -0
  49. package/dist/components/data-table.d.ts.map +1 -0
  50. package/dist/components/data-table.js +50 -0
  51. package/dist/components/date-picker.d.ts +52 -0
  52. package/dist/components/date-picker.d.ts.map +1 -0
  53. package/dist/components/date-picker.js +73 -0
  54. package/dist/components/disclosure.d.ts +31 -0
  55. package/dist/components/disclosure.d.ts.map +1 -0
  56. package/dist/components/disclosure.js +32 -0
  57. package/dist/components/display.d.ts +188 -0
  58. package/dist/components/display.d.ts.map +1 -0
  59. package/dist/components/display.js +190 -0
  60. package/dist/components/empty-state.d.ts +150 -0
  61. package/dist/components/empty-state.d.ts.map +1 -0
  62. package/dist/components/empty-state.js +113 -0
  63. package/dist/components/error-state.d.ts +38 -0
  64. package/dist/components/error-state.d.ts.map +1 -0
  65. package/dist/components/error-state.js +16 -0
  66. package/dist/components/file-upload.d.ts +34 -0
  67. package/dist/components/file-upload.d.ts.map +1 -0
  68. package/dist/components/file-upload.js +34 -0
  69. package/dist/components/gauge.d.ts +47 -0
  70. package/dist/components/gauge.d.ts.map +1 -0
  71. package/dist/components/gauge.js +65 -0
  72. package/dist/components/icon.d.ts +32 -0
  73. package/dist/components/icon.d.ts.map +1 -0
  74. package/dist/components/icon.js +20 -0
  75. package/dist/components/icons.d.ts +19 -0
  76. package/dist/components/icons.d.ts.map +1 -0
  77. package/dist/components/icons.js +39 -0
  78. package/dist/components/input.d.ts +35 -0
  79. package/dist/components/input.d.ts.map +1 -0
  80. package/dist/components/input.js +41 -0
  81. package/dist/components/layout.d.ts +133 -0
  82. package/dist/components/layout.d.ts.map +1 -0
  83. package/dist/components/layout.js +132 -0
  84. package/dist/components/link.d.ts +22 -0
  85. package/dist/components/link.d.ts.map +1 -0
  86. package/dist/components/link.js +2 -0
  87. package/dist/components/live-scope.d.ts +15 -0
  88. package/dist/components/live-scope.d.ts.map +1 -0
  89. package/dist/components/live-scope.js +67 -0
  90. package/dist/components/log-view.d.ts +34 -0
  91. package/dist/components/log-view.d.ts.map +1 -0
  92. package/dist/components/log-view.js +30 -0
  93. package/dist/components/number-input.d.ts +26 -0
  94. package/dist/components/number-input.d.ts.map +1 -0
  95. package/dist/components/number-input.js +57 -0
  96. package/dist/components/overlay.d.ts +257 -0
  97. package/dist/components/overlay.d.ts.map +1 -0
  98. package/dist/components/overlay.js +276 -0
  99. package/dist/components/pagination.d.ts +38 -0
  100. package/dist/components/pagination.d.ts.map +1 -0
  101. package/dist/components/pagination.js +46 -0
  102. package/dist/components/pie.d.ts +40 -0
  103. package/dist/components/pie.d.ts.map +1 -0
  104. package/dist/components/pie.js +74 -0
  105. package/dist/components/primitives.d.ts +96 -0
  106. package/dist/components/primitives.d.ts.map +1 -0
  107. package/dist/components/primitives.js +81 -0
  108. package/dist/components/progress.d.ts +29 -0
  109. package/dist/components/progress.d.ts.map +1 -0
  110. package/dist/components/progress.js +16 -0
  111. package/dist/components/radio-group.d.ts +17 -0
  112. package/dist/components/radio-group.d.ts.map +1 -0
  113. package/dist/components/radio-group.js +23 -0
  114. package/dist/components/rail-link.d.ts +24 -0
  115. package/dist/components/rail-link.d.ts.map +1 -0
  116. package/dist/components/rail-link.js +24 -0
  117. package/dist/components/rich-text.d.ts +51 -0
  118. package/dist/components/rich-text.d.ts.map +1 -0
  119. package/dist/components/rich-text.js +147 -0
  120. package/dist/components/section.d.ts +15 -0
  121. package/dist/components/section.d.ts.map +1 -0
  122. package/dist/components/section.js +8 -0
  123. package/dist/components/segmented.d.ts +13 -0
  124. package/dist/components/segmented.d.ts.map +1 -0
  125. package/dist/components/segmented.js +12 -0
  126. package/dist/components/skeleton.d.ts +69 -0
  127. package/dist/components/skeleton.d.ts.map +1 -0
  128. package/dist/components/skeleton.js +67 -0
  129. package/dist/components/slider.d.ts +18 -0
  130. package/dist/components/slider.d.ts.map +1 -0
  131. package/dist/components/slider.js +15 -0
  132. package/dist/components/sparkline.d.ts +17 -0
  133. package/dist/components/sparkline.d.ts.map +1 -0
  134. package/dist/components/sparkline.js +19 -0
  135. package/dist/components/spinner.d.ts +15 -0
  136. package/dist/components/spinner.d.ts.map +1 -0
  137. package/dist/components/spinner.js +22 -0
  138. package/dist/components/stacked-bar.d.ts +21 -0
  139. package/dist/components/stacked-bar.d.ts.map +1 -0
  140. package/dist/components/stacked-bar.js +41 -0
  141. package/dist/components/surface.d.ts +76 -0
  142. package/dist/components/surface.d.ts.map +1 -0
  143. package/dist/components/surface.js +49 -0
  144. package/dist/components/switch.d.ts +12 -0
  145. package/dist/components/switch.d.ts.map +1 -0
  146. package/dist/components/switch.js +13 -0
  147. package/dist/components/tabs.d.ts +47 -0
  148. package/dist/components/tabs.d.ts.map +1 -0
  149. package/dist/components/tabs.js +12 -0
  150. package/dist/components/timeline-strip.d.ts +19 -0
  151. package/dist/components/timeline-strip.d.ts.map +1 -0
  152. package/dist/components/timeline-strip.js +14 -0
  153. package/dist/components/tone.d.ts +58 -0
  154. package/dist/components/tone.d.ts.map +1 -0
  155. package/dist/components/tone.js +70 -0
  156. package/dist/components/tree-select.d.ts +26 -0
  157. package/dist/components/tree-select.d.ts.map +1 -0
  158. package/dist/components/tree-select.js +59 -0
  159. package/dist/index.d.ts +54 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +70 -0
  162. package/dist/kit.css +2248 -0
  163. package/dist/lib/cn.d.ts +10 -0
  164. package/dist/lib/cn.d.ts.map +1 -0
  165. package/dist/lib/cn.js +12 -0
  166. package/dist/lib/color.d.ts +68 -0
  167. package/dist/lib/color.d.ts.map +1 -0
  168. package/dist/lib/color.js +134 -0
  169. package/dist/lib/palette.d.ts +84 -0
  170. package/dist/lib/palette.d.ts.map +1 -0
  171. package/dist/lib/palette.js +193 -0
  172. package/dist/lib/scroll-lock.d.ts +19 -0
  173. package/dist/lib/scroll-lock.d.ts.map +1 -0
  174. package/dist/lib/scroll-lock.js +33 -0
  175. package/dist/patterns/app-shell.d.ts +36 -0
  176. package/dist/patterns/app-shell.d.ts.map +1 -0
  177. package/dist/patterns/app-shell.js +14 -0
  178. package/dist/patterns/auth-template.d.ts +36 -0
  179. package/dist/patterns/auth-template.d.ts.map +1 -0
  180. package/dist/patterns/auth-template.js +40 -0
  181. package/dist/patterns/detail-template.d.ts +52 -0
  182. package/dist/patterns/detail-template.d.ts.map +1 -0
  183. package/dist/patterns/detail-template.js +11 -0
  184. package/dist/patterns/index.d.ts +18 -0
  185. package/dist/patterns/index.d.ts.map +1 -0
  186. package/dist/patterns/index.js +17 -0
  187. package/dist/patterns/list-template.d.ts +32 -0
  188. package/dist/patterns/list-template.d.ts.map +1 -0
  189. package/dist/patterns/list-template.js +8 -0
  190. package/dist/patterns/overview-template.d.ts +26 -0
  191. package/dist/patterns/overview-template.d.ts.map +1 -0
  192. package/dist/patterns/overview-template.js +8 -0
  193. package/dist/patterns/public-template.d.ts +28 -0
  194. package/dist/patterns/public-template.d.ts.map +1 -0
  195. package/dist/patterns/public-template.js +7 -0
  196. package/dist/patterns/settings-template.d.ts +50 -0
  197. package/dist/patterns/settings-template.d.ts.map +1 -0
  198. package/dist/patterns/settings-template.js +33 -0
  199. package/dist/patterns/shared.d.ts +40 -0
  200. package/dist/patterns/shared.d.ts.map +1 -0
  201. package/dist/patterns/shared.js +29 -0
  202. package/dist/patterns/wizard-template.d.ts +28 -0
  203. package/dist/patterns/wizard-template.d.ts.map +1 -0
  204. package/dist/patterns/wizard-template.js +15 -0
  205. package/dist/styles.css +5 -0
  206. package/dist/theme.css +812 -0
  207. package/package.json +87 -0
package/dist/kit.css ADDED
@@ -0,0 +1,2248 @@
1
+ /* ==============================================================
2
+ tamga-ui · KİT
3
+
4
+ Bileşen sınıfları ve fizik. Saf CSS — Tailwind direktifi yok,
5
+ Tailwind'i olmayan bir projede de çalışır.
6
+
7
+ Token'lar theme.css'te. Bu dosya yalnız onları tüketir.
8
+ ============================================================== */
9
+
10
+ /* ==============================================================
11
+ THE KIT.
12
+
13
+ This was an isolated island (`.taslak`) while it was a reference. It is not a reference any more
14
+ — it is the product's kit, so the scoping came off: `tamga-*` is global, the tokens sit in the same
15
+ @theme as everything else, and the base layer styles the document rather than a wrapper div.
16
+
17
+ What that replaced: a second, shadcn-derived kit under components/ui with twelve components
18
+ duplicated by name. Two kits meant two answers to "what does a button look like", and Storybook
19
+ showed both. One kit now.
20
+ ============================================================== */
21
+
22
+ /* ------------------------------------------------------------------ *
23
+ * Measurements that are NOT on the spacing scale, and why.
24
+ *
25
+ * check-scale forbids invented bracket values and prescribes exactly this escape: name the value
26
+ * next to the tokens with the reason, then use the `var()` form at the call site. Each of these is
27
+ * a mechanic, not a spacing choice — rounding it to a 4px step would break the thing it measures.
28
+ * ------------------------------------------------------------------ */
29
+ /* tamga:sabit-kit — kitin kendi yapısal ölçüleri; temaya bağlı değil. */
30
+ :root {
31
+ /* The gap an overlay floats above its trigger. 8px, so the offset shadow (2px) clears the edge
32
+ with room to read as detached rather than stuck.
33
+ TR: Bir katmanın tetikleyicisinin üstünde durduğu boşluk. 8px, yani 2px'lik kaydırma
34
+ gölgesi kenarı yapışık değil ayrık okunacak kadar rahat geçiyor. */
35
+ --overlay-gap: 8px;
36
+ /* The whole computed offset, not just the gap: check-scale accepts a token reference, and a
37
+ token that still needs arithmetic at the call site is half a token.
38
+ TR: Yalnız boşluk değil hesaplanmış kaydırmanın tamamı: check-scale bir token
39
+ referansını kabul ediyor, ve çağrı yerinde hâlâ aritmetik isteyen bir token yarım
40
+ token. */
41
+ --overlay-below: calc(100% + var(--overlay-gap));
42
+ --overlay-above: calc(100% + var(--overlay-gap));
43
+ /* The dial's band label sits just under the ring's baseline. Negative and tiny: a position, not
44
+ a spacing step.
45
+ TR: Kadranın bant etiketi halkanın taban çizgisinin hemen altında duruyor. Negatif
46
+ ve çok küçük: bir boşluk adımı değil bir konum. */
47
+ --dial-label-drop: -4px;
48
+ /* Room a raised object needs BESIDE itself so its 2px offset is not clipped by the parent's
49
+ overflow. 3px, one more than the offset, which is the clipping trap in §2.
50
+ TR: Yükseltilmiş bir nesnenin, 2px'lik kaydırması üst öğenin taşmasıyla kırpılmasın
51
+ diye YANINDA ihtiyaç duyduğu yer. 3px, kaydırmadan bir fazla; §2'deki kırpılma
52
+ tuzağı tam olarak bu. */
53
+ --offset-room: 3px;
54
+ /* The instrument marks: a 2px tick and a 6px dot. Below the spacing scale on purpose: these are
55
+ read as marks, not as boxes, and 4px is already too heavy for a dial tick.
56
+ TR: Gösterge işaretleri: 2px'lik bir çentik ve 6px'lik bir nokta. Boşluk ölçeğinin
57
+ altında, bilerek: bunlar kutu değil işaret olarak okunuyor, ve 4px bir kadran
58
+ çentiği için şimdiden ağır. */
59
+ --mark-tick: 2px;
60
+ --mark-dot: 6px;
61
+
62
+ /* Adım işaretinin karesi ve iki adımı bağlayan çizginin uzunluğu. Bir ölçek
63
+ adımı değil: işaret, iki haneli bir sayı ile bir çentik glifinin İKİSİNİ
64
+ birden tutması gereken en küçük kare. Sabit olması şart, çünkü adım
65
+ tamamlandığında büyüyen bir işaret şeridin tamamını kaydırıyor.
66
+ TR: Adım işaretinin karesi ve iki adımı bağlayan çizginin uzunluğu. Bir
67
+ ölçek adımı değil: iki haneli bir sayıyı da çentik glifini de tutabilen en
68
+ küçük kare. Sabit olmak zorunda, yoksa tamamlanan adım şeridi kaydırıyor. */
69
+ --step-mark: 24px;
70
+ --step-line: 28px;
71
+ /* Smallest radius in the system: the checkbox tick and the skeleton block. --radius-ctl (4px)
72
+ reads as a control; these are neither.
73
+ TR: Sistemdeki en küçük yarıçap: onay kutusunun çentiği ve iskelet bloğu.
74
+ --radius-ctl (4px) bir kontrol gibi okunuyor; bunların ikisi de kontrol değil. */
75
+ --radius-mark: 3px;
76
+ /* The ceiling for a wide dialog. Not a scale step but a LIMIT: a preview
77
+ should be able to take the whole screen and still not grow without end,
78
+ because at 2000px a list stops being readable. 3rem is the shell's own
79
+ margin.
80
+ TR: Geniş diyaloğun tavanı. Bir ölçek adımı değil bir SINIR: bir önizleme ekranın
81
+ tamamını alabilmeli ama sonsuz da genişlememeli, çünkü 2000 pikselde bir liste artık
82
+ okunmuyor. 3rem, kabuğun kendi kenar boşluğu. */
83
+ --dialog-wide: min(96rem, calc(100vw - 3rem));
84
+
85
+ /* Where a dense table stops shrinking and starts scrolling. Content-driven, not a scale step:
86
+ it is the width at which the narrowest column stops being readable.
87
+ TR: Sık bir tablonun küçülmeyi bırakıp kaydırmaya başladığı yer. Bir ölçek adımı
88
+ değil içerikten gelen bir sayı: en dar sütunun okunmaz olduğu genişlik. */
89
+ --table-min: 720px;
90
+ --table-min-wide: 980px;
91
+ --table-min-widest: 1080px;
92
+ }
93
+
94
+ /* tamga:kit-koyu */
95
+ .dark {
96
+
97
+ color-scheme: dark;
98
+
99
+ /* The neutral ramp is tinted toward the accent hue, specified in LCh — never
100
+ in HSL, and never by copying another product's numbers. Two earlier attempts
101
+ failed and both failures are the reason this comment is long.
102
+
103
+ 1st: matched Sentry's HSL saturation (14%). HSL S is not perceptual chroma —
104
+ at identical S and L, violet at 258° carries ~1.4x the real chroma of blue.
105
+ Matching their number guaranteed a weaker tint. The ramp read grey.
106
+
107
+ 2nd: rebuilt in LCh at hue 277° with raised chroma. Still grey, for a worse
108
+ reason: 277° is the middle of the Tailwind slate band (271–283°), and the
109
+ chroma sat under slate's own (slate-900 is C* 14.45). A colour inside that
110
+ hue band and below that chroma IS the industry-default dark grey. Raising
111
+ chroma alone had only made it a more saturated slate.
112
+
113
+ What actually makes a tint read as a tint is hue distance from the neutral
114
+ band, not chroma. Sentry gets away with LOW chroma (5.5–12.7) because its
115
+ hue sits at 299–304° — about 20° clear of slate, where nothing generic
116
+ lives. Our blue does not have that luxury: blue IS the neutral band.
117
+
118
+ So: hue 291°, the true LCh hue of our own primary #12245c, which clears
119
+ slate's edge — and chroma pushed past slate's 14.45 to cover the fact that
120
+ we clear it by 8° where Sentry clears it by 20°. Exceeding Sentry's chroma
121
+ is not an inconsistency; it is what equal perception costs on a blue axis.
122
+ L* is pinned exactly where it already sat, so the ramp gains colour without
123
+ moving a single contrast value.
124
+ L* 5.9 -> C* 16 L* 19.9 -> C* 26 L* 85.1 -> C* 6.0
125
+ L* 9.3 -> C* 19 L* 27.9 -> C* 31 L* 94.4 -> C* 2.8
126
+ L* 14.2 -> C* 22 L* 61.9 -> C* 11
127
+ Light stays warm paper on purpose: paper by day, instrument screen by night,
128
+ and a cool ground makes the warm status colours separate harder. */
129
+ --color-page: #0f1127;
130
+ /* A raised surface: cards, overlays, popovers. It is barely lighter than the
131
+ page on purpose; the edge does the lifting. */
132
+ --color-shell: #131832;
133
+ /* A row under the pointer, and a selected row's neutral wash. Same value as
134
+ sunk, different job. */
135
+ --color-hover: #1c2241;
136
+ /* Rail and band ARE the page in both themes — a 1px line does the dividing,
137
+ never a fill. Dark briefly had them 1.9 ΔL* off the page: too small to read
138
+ as a surface, too large to be the same one. */
139
+ --color-band: #0f1127;
140
+ /* The navigation rail. Also the page: a sidebar that fills itself becomes a
141
+ second surface competing with the content. */
142
+ --color-rail: #0f1127;
143
+ /* Content that sits IN the page rather than on it: filter bars, table
144
+ headers, code blocks, sub-panels. */
145
+ --color-sunk: #1c2241;
146
+ --color-line: #262d54;
147
+ --color-edge: #363e6e;
148
+ --color-chart-fill: #1c2241;
149
+ --color-ink-faint: #9494a8;
150
+ /* The second weight: labels, secondary rows, anything that supports the
151
+ heading without competing with it. */
152
+ --color-ink-soft: #d4d4df;
153
+ /* The heaviest weight: headings, table values, anything the eye should land
154
+ on first. */
155
+ --color-ink: #efeef4;
156
+
157
+ /* Dark inverts the face, and it has to. The rule across both themes is that
158
+ the FACE is the layer furthest from the ground and the BASE is the brand
159
+ navy. Light's ground is paper, so the face goes mid-dark and carries white
160
+ ink. Dark's ground is near-black, and there white ink is what kills the
161
+ press: it caps the fill at L* 47, while the base has to reach L* 26 just to
162
+ separate from the page — leaving at most 21 ΔL* between the two layers,
163
+ less than light already has. Dark ink lifts that ceiling, the face goes to
164
+ L* 66, and the layers separate by 50. Hue sits at 272° rather than light's
165
+ 284° to stay plainly blue at high lightness, where 284° starts to read
166
+ periwinkle. */
167
+ --color-accent: #53a4fa;
168
+ --color-accent-hover: #78b4ff; /* lightens here — dark ink wants it brighter */
169
+ --color-accent-active: #4097eb;
170
+ --color-accent-ink: #12245c; /* navy ink on the bright face — 5.62:1 */
171
+ --color-accent-line: #86a7f3;
172
+ /* Must out-chroma the neutral hover it sits next to. At the old C* 16.3 it
173
+ fell UNDER hover's C* 22, so a selected row read duller than a hovered one. */
174
+ --color-accent-bg: #0f2458;
175
+ /* The brand navy LIFTED, and the lift is the whole point. A base has to clear
176
+ two things at once — the face above it and the ground behind it — and light
177
+ manages both without help (79 ΔL* off the paper, 29 off the face). Dropped
178
+ into dark unchanged, #12245c clears the face by 50 but the page by only 10,
179
+ so it sinks into the page and the button loses its base entirely. Being
180
+ bright next to the face is not enough; it also has to be visibly NOT the
181
+ page. L* 30 buys 24 off the page and still leaves 36 off the face. */
182
+ --color-accent-shadow: #344479;
183
+
184
+ /* Status stays warm against the cool ground — deviation separates harder. */
185
+ --color-critical: #ff9478;
186
+ /* The wash behind a critical chip or row. A tint, not a fill: it carries no
187
+ text of its own. */
188
+ --color-critical-bg: #331a12;
189
+ /* Not broken yet, but heading there. A threshold crossed, a queue growing, a
190
+ stock running low. */
191
+ --color-warn: #e8ab3c;
192
+ /* The wash behind a warning chip or row. */
193
+ --color-warn-bg: #33260f;
194
+ /* Healthy, and also resolved. One green for both, because a reader should
195
+ not have to learn two. */
196
+ --color-resolved: #4fd4ac;
197
+ /* The wash behind a healthy or resolved chip. */
198
+ --color-resolved-bg: #0f2a22;
199
+ --color-silent: #9494a8;
200
+ --color-tick: #3f4880;
201
+ --color-chart-1: #ada072;
202
+ /* Teal, the second series. Far enough from resolved's green that a line is
203
+ never mistaken for a status. */
204
+ --color-chart-2: #5badaf;
205
+ /* Blue, the third series. */
206
+ --color-chart-3: #6ea7cc;
207
+ /* Violet, the fourth series. */
208
+ --color-chart-4: #a49ac9;
209
+ /* Rose, the fifth and last. Beyond five the eye stops telling series apart
210
+ and a legend stops helping. */
211
+ --color-chart-5: #ca90a9;
212
+
213
+ /* Same LCh treatment — these echo ramp steps and must not drift off the axis. */
214
+ --color-nav-idle: #89899d;
215
+ /* A rail item under the pointer. Darker than idle in light, brighter in
216
+ dark: the label steps toward the reader. */
217
+ --color-nav-hover: #d4d4df;
218
+ /* The wash behind a hovered rail item. The current item takes an outline
219
+ instead, never this fill. */
220
+ --color-nav-hover-bg: #1c2241;
221
+ --color-scrim: rgba(6, 6, 22, 0.62);
222
+ }
223
+
224
+ @layer base {
225
+
226
+ body {
227
+ background: var(--color-shell);
228
+ /* PUNTO DA BURADA, ve uzun süre değildi.
229
+ Kit `--text-body: 13px`i "THE default: rows, cells, body copy" diye
230
+ tanımlıyordu ama hiçbir yer onu BODY'ye uygulamıyordu. Sonuç: sınıfı
231
+ olan her metin ölçekte, sınıfı olmayan her metin tarayıcının 16px'inde,
232
+ yani aynı ekranda iki ayrı ölçek. Doküman sitesinde görünmedi çünkü o
233
+ kendi okuma ölçeğini yazıyor; bir tüketici panelinde tablo hücreleri %23
234
+ büyük çıktı ve fark oradan yakalandı.
235
+ Tüketici kendi ölçeğini isterse yine yazabilir; bu bir varsayılan. */
236
+ font-size: var(--text-body);
237
+ line-height: 1.5;
238
+ font-variant-numeric: tabular-nums;
239
+ color: var(--color-ink-soft);
240
+ -webkit-font-smoothing: antialiased;
241
+ }
242
+ :is(h1, h2, h3, h4, h5, h6, th, strong, b) {
243
+ color: var(--color-ink);
244
+ }
245
+ * {
246
+ scrollbar-width: thin;
247
+ scrollbar-color: var(--color-edge) transparent;
248
+ }
249
+ *::-webkit-scrollbar {
250
+ width: 8px;
251
+ height: 8px;
252
+ }
253
+ *::-webkit-scrollbar-thumb {
254
+ background: var(--color-edge);
255
+ }
256
+ *::-webkit-scrollbar-track {
257
+ background: transparent;
258
+ }
259
+ /* native checkboxes are restyled so tables and the kit look identical */
260
+ input[type="checkbox"] {
261
+ appearance: none;
262
+ -webkit-appearance: none;
263
+ display: inline-grid;
264
+ place-content: center;
265
+ width: 18px;
266
+ height: 18px;
267
+ margin: 0;
268
+ border: 1px solid var(--color-edge);
269
+ border-radius: 3px;
270
+ background: var(--color-shell);
271
+ box-shadow: 1px 1px 0 var(--color-edge);
272
+ cursor: pointer;
273
+ transition: background var(--duration-press) var(--ease-standard), border-color var(--duration-press) var(--ease-standard), box-shadow var(--duration-press) var(--ease-standard);
274
+ }
275
+ input[type="checkbox"]::before {
276
+ content: "";
277
+ width: 10px;
278
+ height: 10px;
279
+ transform: scale(0);
280
+ transition: transform var(--duration-press) var(--ease-standard);
281
+ background: var(--color-accent-ink);
282
+ clip-path: polygon(
283
+ 14% 47%, 0 61%, 39% 100%, 100% 22%, 85% 8%, 37% 70%
284
+ );
285
+ }
286
+ input[type="checkbox"]:hover {
287
+ border-color: var(--color-accent-line);
288
+ box-shadow: 1px 1px 0 var(--color-accent-line);
289
+ }
290
+ input[type="checkbox"]:checked {
291
+ background: var(--color-accent);
292
+ border-color: var(--color-accent);
293
+ box-shadow: 1px 1px 0 var(--color-accent-shadow);
294
+ }
295
+ input[type="checkbox"]:checked::before {
296
+ transform: scale(1);
297
+ }
298
+ ::selection {
299
+ background: var(--color-accent-bg);
300
+ color: var(--color-ink);
301
+ }
302
+ :focus-visible {
303
+ outline: 1px solid var(--color-accent-line);
304
+ outline-offset: 2px;
305
+ }
306
+ }
307
+
308
+ @layer components {
309
+
310
+ /* ---------- structure ---------- */
311
+
312
+ /* the one left line every page shares */
313
+ .tamga-gutter {
314
+ padding-left: var(--gutter);
315
+ padding-right: var(--gutter);
316
+ }
317
+
318
+ /* a full-bleed rule, still used for page bands and inside cards */
319
+ .tamga-section {
320
+ border-bottom: 1px solid var(--color-line);
321
+ }
322
+
323
+ /* a card is a button that grew: same 1px edge, same soft 2px offset */
324
+ /* The working surface: one card that holds a whole screen.
325
+ The chrome around it — rail, header, ground — is all --color-page and has
326
+ no dividing line, so it reads as one continuous frame. This is the only
327
+ thing on top of that frame, which is why it takes the control radius
328
+ rather than the card radius: at this size the sharper corner keeps it
329
+ reading as a plane, and it stays in the same family as the buttons inside
330
+ it. A softer corner here would make the whole screen look like a widget. */
331
+ .tamga-surface {
332
+ background: var(--color-page);
333
+ border: 1px solid var(--color-edge);
334
+ border-radius: var(--radius-ctl);
335
+ }
336
+
337
+ .tamga-card {
338
+ border-radius: var(--radius-card);
339
+ border: 1px solid var(--color-edge);
340
+ background: var(--color-shell);
341
+ box-shadow: 2px 2px 0 var(--color-edge);
342
+ overflow: hidden;
343
+ }
344
+ /* Kırpmayı kapatan kaçış yolu.
345
+
346
+ `.tamga-card`'ın `overflow: hidden`'ı tabloların köşe yuvarlaması için
347
+ gerekli — ve aynı satır, kartın İÇİNDE açılan her paneli kesiyor:
348
+ bir Combobox listesi, bir DatePicker takvimi, bir satır menüsü. Panel
349
+ çalışıyor, tıklanıyor, ama yarısı görünmüyor. Z-index bunu çözmez;
350
+ hiçbir yığın sırası bir `overflow` kırpmasını aşamaz.
351
+
352
+ `CellActions` aynı tuzağın tablo hücresi karşılığıydı ve ayrı çözülmüştü;
353
+ bu, kart seviyesindeki hâli. */
354
+ .tamga-card-open {
355
+ overflow: visible;
356
+ }
357
+
358
+ /* a second column, opened by one full-height rule */
359
+ .tamga-col {
360
+ border-left: 1px solid var(--color-line);
361
+ }
362
+
363
+ /* section heading sits on the gutter with no box of its own */
364
+ .tamga-head {
365
+ display: flex;
366
+ flex-wrap: wrap;
367
+ align-items: center;
368
+ gap: 16px;
369
+ min-height: var(--row-sm);
370
+ padding-top: 8px;
371
+ padding-bottom: 8px;
372
+ }
373
+
374
+ /* rail item: outlined and lifted when active — never a solid fill */
375
+ /*
376
+ * KAYAN BİR RAY, YÜKSELEN BİR KONTROLÜ KIRPAR.
377
+ *
378
+ * Ölçülen hata: menüsü uzun olan bir kenar çubuğunda `overflow-y: auto`
379
+ * gerekiyor, ama CSS bir eksende `visible` dışında bir değer görünce ÖTEKİ
380
+ * ekseni de `auto`ya çeviriyor. Yani dikey kaydırma isteyen bir kap yatayda
381
+ * da kırpıyor, ve seçili ray girişinin 2px sert kaydırması tam kenarda
382
+ * kesiliyor: düğme sağdan ve alttan eksik görünüyor. Hover'da kaydırma 3px'e
383
+ * çıkıyor, basılınca yer değiştiriyor — üçü de kırpılıyor.
384
+ *
385
+ * Çözüm z-index DEĞİL: hiçbir yığın sırası bir `overflow` kırpmasını aşmaz.
386
+ * Kaydırmanın sığacağı yer AÇILIYOR (`--offset-room`, kaydırmadan bir fazla).
387
+ */
388
+ .tamga-rail-scroll {
389
+ min-height: 0;
390
+ overflow-y: auto;
391
+ /* DÖRT YANDAN DA YER, yalnız sağ ve alttan değil.
392
+ İlk hâli kaydırma gölgesini (sağ+alt) düşünüp hover'ı unutmuştu: seçili
393
+ giriş hover'da `translate(-1px, -1px)` ile YUKARI ve SOLA kayıyor, ve
394
+ sol kenarda yer olmadığı için oradan kesiliyordu. Simetrik dolgu ayrıca
395
+ ikinci bir şeyi düzeltiyor: dar rayda `items-center` artık gerçekten
396
+ ortalıyor — tek yönlü dolgu ikonları 1.5 piksel sola çekiyordu. */
397
+ padding: var(--offset-room);
398
+ /* Kaydırma çubuğu gizli: ray dar ve bir çubuk orada ikonların yerini
399
+ daraltıyor. Kaydırma yine çalışıyor. */
400
+ scrollbar-width: none;
401
+ }
402
+ .tamga-rail-scroll::-webkit-scrollbar {
403
+ display: none;
404
+ }
405
+
406
+ .tamga-rail-link {
407
+ position: relative;
408
+ display: flex;
409
+ height: 40px;
410
+ width: 40px;
411
+ flex-shrink: 0;
412
+ align-items: center;
413
+ justify-content: center;
414
+ border: 1px solid transparent;
415
+ border-radius: var(--radius-ctl);
416
+ color: var(--color-nav-idle);
417
+ transition: color var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard), border-color var(--duration-press) var(--ease-standard),
418
+ box-shadow var(--duration-press) var(--ease-standard), transform var(--duration-press) var(--ease-standard);
419
+ }
420
+ /* ETİKETLİ HÂLİ. Aynı kontrolün ikinci genişliği, ayrı bir bileşen değil:
421
+ dar ray yalnız ikon taşır, geniş kenar çubuğu ikonun yanına etiketi açar.
422
+ İkiye bölünseydi hover, seçili ve odak fizikleri iki yerde yaşardı ve
423
+ zamanla ayrışırdı. */
424
+ .tamga-rail-link-wide {
425
+ width: 100%;
426
+ justify-content: flex-start;
427
+ gap: 10px;
428
+ padding-inline: 10px;
429
+ }
430
+ .tamga-rail-link:hover {
431
+ color: var(--color-nav-hover);
432
+ background: var(--color-nav-hover-bg);
433
+ }
434
+ .tamga-rail-link[data-active="true"] {
435
+ color: var(--color-accent-line);
436
+ background: var(--color-shell);
437
+ border-color: var(--color-accent-line);
438
+ box-shadow: 2px 2px 0 var(--color-accent-line);
439
+ }
440
+ .tamga-rail-link[data-active="true"]:hover {
441
+ background: var(--color-shell);
442
+ color: var(--color-accent-line);
443
+ transform: translate(-1px, -1px);
444
+ box-shadow: 3px 3px 0 var(--color-accent-line);
445
+ }
446
+ .tamga-rail-link[data-active="true"]:active {
447
+ transform: translate(2px, 2px);
448
+ box-shadow: 0 0 0 var(--color-accent-line);
449
+ }
450
+
451
+ /* ------------------------------------------------------------------ *
452
+ * Press snaps, release eases.
453
+ *
454
+ * Both directions used the same 100ms ease, and on a trackpad TAP that meant the press was never
455
+ * seen: `:active` lasts about 10–40ms on a tap, so the control got a third of the way down and
456
+ * turned round. Holding a physical click gives it the full 100ms, which is why the same button
457
+ * felt right under a click and dead under a tap.
458
+ *
459
+ * So the press is instant and only the RETURN is animated. Even a 15ms tap now bottoms the
460
+ * control out completely, and the 100ms float back is the part the eye actually reads as a
461
+ * click — perception needs the movement, not the dwell.
462
+ *
463
+ * Only the duration changes. Travel, shadow and colour are untouched.
464
+ * ------------------------------------------------------------------ */
465
+ .tamga-btn:not(:disabled):active,
466
+ .tamga-icon-btn:not(:disabled):active,
467
+ .tamga-mini-btn:not(:disabled):active,
468
+ .tamga-rail-link[data-active="true"]:active,
469
+ .tamga-art-tile:active,
470
+ .tamga-art-route:active {
471
+ transition-duration: 0s;
472
+ }
473
+
474
+ /* ------------------------------------------------------------------ *
475
+ * The press-travel hit fix.
476
+ *
477
+ * A raised control moves down-right when pressed — hover sits at −1,−1 and active at +2,+2 (the
478
+ * primary goes to +3,+3), so the box travels 3–4px out from under the pointer. A transform moves
479
+ * the HIT AREA with it, so a pointer resting within that band along the top or left edge ends up
480
+ * over the parent by the time the button comes up. `mousedown` lands on the button, `mouseup`
481
+ * lands on its container, and a `click` event is never born — the control simply does nothing.
482
+ *
483
+ * It shows up on a trackpad TAP far more than on a physical click: a tap has no dwell time and
484
+ * the finger drifts a pixel or two, while a real click holds the pointer still while the box
485
+ * moves. Same defect, different odds — which is exactly why it read as "tap is broken".
486
+ *
487
+ * The fix is a hit extender that reaches back up and left by the travel distance. It rides along
488
+ * with the transform, so once the control has moved it covers precisely the ground the control
489
+ * left behind. It only ever grows up and left, never down or right, so it cannot reach into the
490
+ * next control in a row or a column.
491
+ * ------------------------------------------------------------------ */
492
+ .tamga-btn,
493
+ .tamga-icon-btn,
494
+ .tamga-mini-btn,
495
+ .tamga-rail-link {
496
+ position: relative;
497
+ }
498
+ .tamga-btn::after,
499
+ .tamga-icon-btn::after,
500
+ .tamga-mini-btn::after,
501
+ .tamga-rail-link::after {
502
+ content: "";
503
+ position: absolute;
504
+ /* 4px covers the deepest travel in the kit (the primary's +3 from a −1 hover). */
505
+ inset: -4px 0 0 -4px;
506
+ border-radius: inherit;
507
+ }
508
+
509
+ /* ---------- raised objects ---------- */
510
+ /* buttons and the few genuinely detached cards share one soft edge:
511
+ a 1px rule plus the same rule offset by 2px. No hard black. */
512
+
513
+ .tamga-raised {
514
+ border-radius: var(--radius-card);
515
+ border: 1px solid var(--color-edge);
516
+ background: var(--color-shell);
517
+ box-shadow: 2px 2px 0 var(--color-edge);
518
+ }
519
+
520
+ /* ------------------------------------------------------------------ *
521
+ * TWO PHYSICS, ONE GRAMMAR.
522
+ *
523
+ * RAISED — sits on top of the page, and pressing puts it back down.
524
+ * 1px edge + N px hard offset; press travels the offset and collapses it.
525
+ * Buttons, cards, rail items, segments, the primary action.
526
+ *
527
+ * SEATED — is part of its surface and never lifts at all. It answers by
528
+ * FILLING or by drawing a rule, not by travelling.
529
+ * Tabs (underline), switches (filled track), checkboxes and radios
530
+ * (filled box), chips (edge only — and chips are not even interactive).
531
+ *
532
+ * The split is not decoration. A tab is not an object lying on the page,
533
+ * it is part of the bar; pushing it "down" would be a lie about what it is.
534
+ * v5 inherits the same two-physics idea under the names .press-surface and
535
+ * .seated-surface — the grammar matches even though the surfaces differ.
536
+ *
537
+ * The rule that follows from it: DEPTH MEANS PRESSABLE. Anything that
538
+ * cannot be pressed does not get an offset, which is exactly why the chip
539
+ * lost the one it briefly had.
540
+ * ------------------------------------------------------------------ */
541
+
542
+ .tamga-btn {
543
+ border-radius: var(--radius-ctl);
544
+ display: inline-flex;
545
+ align-items: center;
546
+ justify-content: center;
547
+ gap: 8px;
548
+ height: var(--control);
549
+ padding: 0 16px;
550
+ border: 1px solid var(--color-edge);
551
+ background: var(--color-shell);
552
+ font-size: var(--text-body);
553
+ font-weight: 500;
554
+ color: var(--color-ink);
555
+ box-shadow: 2px 2px 0 var(--color-edge);
556
+ transition: transform var(--duration-press) var(--ease-standard), box-shadow var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard),
557
+ border-color var(--duration-press) var(--ease-standard);
558
+ cursor: pointer;
559
+ white-space: nowrap;
560
+ }
561
+ /* a real button: lifts under the cursor, presses flat when clicked */
562
+ .tamga-btn:not(:disabled):hover {
563
+ background: var(--color-hover);
564
+ transform: translate(-1px, -1px);
565
+ box-shadow: 3px 3px 0 var(--color-edge);
566
+ }
567
+ .tamga-btn:not(:disabled):active {
568
+ transform: translate(2px, 2px);
569
+ box-shadow: 0 0 0 var(--color-edge);
570
+ }
571
+ /* KAPALI BİR DÜĞME YASAK DEĞİL, ATIL. `not-allowed` imleci "buna hakkın
572
+ yok" diyor; oysa kaydet düğmesi değişiklik olmadığı için kapalı, ve
573
+ kullanıcı bir şey yanlış yapmış değil. İmleç bir suçlama taşımamalı. */
574
+ /* SÜRERKEN YÜKSELMİYOR AMA SOLMUYOR DA. `disabled` görünümü "bu düğme
575
+ kapalı" der; oysa çalışan bir düğme kapalı değil MEŞGUL, ve soldurmak
576
+ kullanıcıya işin durduğunu düşündürüyor. Spinner zaten durumu söylüyor. */
577
+ /* `:disabled` de eşleşiyor (kilit onunla kuruluyor) ve aynı özgüllükte,
578
+ sonra geldiği için kazanıyordu: meşgul bir düğme "atıl" imleci
579
+ gösteriyordu. İkisi birden yazılıyor, böylece sıra değil anlam kazanıyor. */
580
+ .tamga-btn[data-busy]:disabled {
581
+ opacity: 1;
582
+ cursor: progress;
583
+ }
584
+ .tamga-btn[data-busy] > [role="status"] {
585
+ position: absolute;
586
+ }
587
+ .tamga-btn[data-busy] {
588
+ position: relative;
589
+ }
590
+
591
+ .tamga-btn:disabled {
592
+ opacity: 0.45;
593
+ cursor: default;
594
+ }
595
+
596
+ /* The edge is the BASE colour, not the pressed colour. A keycap and the base
597
+ it sits on are one object seen from two sides, so they share an outline —
598
+ that is what stops a bright face from floating free of its own shadow. */
599
+ .tamga-btn-primary {
600
+ background: var(--color-accent);
601
+ border-color: var(--color-accent-shadow);
602
+ color: var(--color-accent-ink);
603
+ box-shadow: 3px 3px 0 var(--color-accent-shadow);
604
+ }
605
+ .tamga-btn-primary:not(:disabled):hover {
606
+ background: var(--color-accent-hover);
607
+ transform: translate(-1px, -1px);
608
+ box-shadow: 4px 4px 0 var(--color-accent-shadow);
609
+ }
610
+ .tamga-btn-primary:not(:disabled):active {
611
+ background: var(--color-accent-active);
612
+ transform: translate(3px, 3px);
613
+ box-shadow: 0 0 0 var(--color-accent-shadow);
614
+ }
615
+
616
+ /* Success and danger are the same object in two colours: outlined, never
617
+ filled. The fill belongs to the one primary action on a page — publishing a
618
+ draft and deleting a record are both important, but neither is THE
619
+ action, and filling them would put two shouting buttons in one view.
620
+ This rule was missing entirely for a while: the class was used in five
621
+ places and defined in none, so every "success" button silently rendered as
622
+ a plain neutral one. Green belongs to resolved, which is exactly what this
623
+ button does — it is the one place the resolved colour touches a control. */
624
+ .tamga-btn-success {
625
+ background: var(--color-shell);
626
+ border-color: var(--color-resolved);
627
+ color: var(--color-resolved);
628
+ box-shadow: 2px 2px 0 var(--color-resolved);
629
+ }
630
+ .tamga-btn-success:not(:disabled):hover {
631
+ background: var(--color-resolved-bg);
632
+ box-shadow: 3px 3px 0 var(--color-resolved);
633
+ }
634
+ .tamga-btn-success:not(:disabled):active {
635
+ box-shadow: 0 0 0 var(--color-resolved);
636
+ }
637
+
638
+ .tamga-btn-danger {
639
+ background: var(--color-shell);
640
+ border-color: var(--color-critical);
641
+ color: var(--color-critical);
642
+ box-shadow: 2px 2px 0 var(--color-critical);
643
+ }
644
+ .tamga-btn-danger:not(:disabled):hover {
645
+ background: var(--color-critical-bg);
646
+ box-shadow: 3px 3px 0 var(--color-critical);
647
+ }
648
+ .tamga-btn-danger:not(:disabled):active {
649
+ box-shadow: 0 0 0 var(--color-critical);
650
+ }
651
+
652
+ /* The one member of the family that is not an object on the page: no edge,
653
+ no offset, and therefore no lift. Cancel and dismiss have to stay out of
654
+ the way, and anything raised competes with the action beside it.
655
+ This rule was missing entirely — the class was used and never written, so
656
+ every ghost button silently rendered as a full secondary button, edge and
657
+ offset included. The same hole .tamga-btn-success had. */
658
+ .tamga-btn-ghost {
659
+ background: transparent;
660
+ border-color: transparent;
661
+ box-shadow: none;
662
+ color: var(--color-ink-soft);
663
+ }
664
+ .tamga-btn-ghost:not(:disabled):hover {
665
+ background: var(--color-hover);
666
+ border-color: transparent;
667
+ box-shadow: none;
668
+ transform: none;
669
+ color: var(--color-ink);
670
+ }
671
+ .tamga-btn-ghost:not(:disabled):active {
672
+ background: var(--color-sunk);
673
+ box-shadow: none;
674
+ transform: none;
675
+ }
676
+
677
+ /* A button that reads as a link. Same element, same keyboard behaviour, no
678
+ surface at all — for the rare navigation that sits in a row of buttons and
679
+ must not compete with them.
680
+ It is a BUTTON, not an <a>: use this when the click runs code. If it
681
+ navigates, use a real anchor with .tamga-link, because a link that is not an
682
+ anchor cannot be opened in a new tab and is a lie to a screen reader. */
683
+ .tamga-btn-link {
684
+ height: auto;
685
+ padding: 0;
686
+ background: transparent;
687
+ border-color: transparent;
688
+ box-shadow: none;
689
+ color: var(--color-accent-line);
690
+ text-underline-offset: 3px;
691
+ }
692
+ .tamga-btn-link:not(:disabled):hover {
693
+ background: transparent;
694
+ border-color: transparent;
695
+ box-shadow: none;
696
+ transform: none;
697
+ text-decoration: underline;
698
+ }
699
+ .tamga-btn-link:not(:disabled):active {
700
+ background: transparent;
701
+ box-shadow: none;
702
+ transform: none;
703
+ }
704
+
705
+ .tamga-btn-sm {
706
+ height: 32px;
707
+ padding: 0 8px;
708
+ font-size: var(--text-small);
709
+ }
710
+
711
+ .tamga-icon-btn {
712
+ border-radius: var(--radius-ctl);
713
+ display: inline-flex;
714
+ align-items: center;
715
+ justify-content: center;
716
+ width: var(--control);
717
+ height: var(--control);
718
+ border: 1px solid var(--color-edge);
719
+ background: var(--color-shell);
720
+ color: var(--color-ink);
721
+ box-shadow: 2px 2px 0 var(--color-edge);
722
+ transition: transform var(--duration-press) var(--ease-standard), box-shadow var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard);
723
+ cursor: pointer;
724
+ }
725
+ .tamga-icon-btn:not(:disabled):hover {
726
+ background: var(--color-hover);
727
+ transform: translate(-1px, -1px);
728
+ box-shadow: 3px 3px 0 var(--color-edge);
729
+ }
730
+ .tamga-icon-btn:not(:disabled):active {
731
+ transform: translate(2px, 2px);
732
+ box-shadow: 0 0 0 var(--color-edge);
733
+ }
734
+
735
+ /* Sayı girdisinin artır/azalt okları.
736
+
737
+ `mini-btn` KULLANILAMAZ ve ilk sürüm onu kullanıyordu: o 32×32 bir
738
+ kontrol, ikisi üst üste 65px eder, girdi ise 40px. Oklar kutunun dışına
739
+ taşıyordu — çalışıyorlardı ama alanın içinde durmuyorlardı.
740
+
741
+ Bir spinner ayrı bir şekildir: alanın SAĞ KENARINA yapışır, yüksekliğini
742
+ ondan alır ve kendi kutusu yoktur — girdiden tek bir dikey kuralla
743
+ ayrılır. Yükselmez; bu boyutta bir offset kenardan taşar (mini butonun
744
+ yükselmemesiyle aynı gerekçe, Yasa 1'in boyut istisnası). */
745
+ /* Sayfalayıcının sayfa düğmesi.
746
+
747
+ `.tamga-option` KULLANILAMAZ ve ilk sürüm onu kullanıyordu — üstelik
748
+ kodun yanındaki yorumda "seçili sayfa dolgu almaz" yazarken. Yorum
749
+ doğruydu, sınıf yalanlıyordu: `.tamga-option[data-selected]` bir DOLGU ve
750
+ sola oturan bir iç iz veriyor. Sonuç ekranda üç ayrı yanlış oldu — dolu
751
+ bir blok, sayının solunda başıboş duran dikey bir çizgi, ve `width: 100%`
752
+ olduğu için esneyen bir kutu.
753
+
754
+ Sayfa düğmesi rail'in dilini konuşur: seçili olan YÜKSELMEZ, oturur —
755
+ kabuk zemini, aksan kenarı, 2px sert offset. Yasa 2: "buradasın" bir
756
+ eylem değildir. */
757
+ .tamga-page {
758
+ /* İMLEÇ. Kitteki her tıklanabilir sınıf bunu taşıyor (`btn`, `icon-btn`,
759
+ `mini-btn`, `tab`, `segment`, `option`, `switch`); sayfalayıcı tek
760
+ istisnaydı ve bu bir gözden kaçmaydı. Tailwind v4 önyüklemesi artık
761
+ `button`a imleç vermiyor, yani eksiklik doğrudan görünüyor. */
762
+ cursor: pointer;
763
+ display: inline-flex;
764
+ height: 40px;
765
+ min-width: 40px;
766
+ flex-shrink: 0;
767
+ align-items: center;
768
+ justify-content: center;
769
+ padding: 0 8px;
770
+ border: 1px solid transparent;
771
+ border-radius: var(--radius-ctl);
772
+ color: var(--color-ink-soft);
773
+ font-size: var(--text-body);
774
+ transition:
775
+ background var(--duration-press) var(--ease-standard),
776
+ color var(--duration-press) var(--ease-standard),
777
+ border-color var(--duration-press) var(--ease-standard),
778
+ box-shadow var(--duration-press) var(--ease-standard);
779
+ }
780
+ .tamga-page:hover {
781
+ background: var(--color-hover);
782
+ color: var(--color-ink);
783
+ }
784
+ .tamga-page[aria-current="page"] {
785
+ background: var(--color-shell);
786
+ border-color: var(--color-accent-line);
787
+ box-shadow: 2px 2px 0 var(--color-accent-line);
788
+ color: var(--color-accent-line);
789
+ font-weight: 500;
790
+ }
791
+ .tamga-page[aria-current="page"]:hover {
792
+ background: var(--color-shell);
793
+ color: var(--color-accent-line);
794
+ }
795
+
796
+ .tamga-stepper {
797
+ position: absolute;
798
+ top: 1px;
799
+ right: 1px;
800
+ bottom: 1px;
801
+ display: flex;
802
+ width: 22px;
803
+ flex-direction: column;
804
+ overflow: hidden;
805
+ border-left: 1px solid var(--color-edge);
806
+ border-radius: 0 var(--radius-ctl) var(--radius-ctl) 0;
807
+ }
808
+ .tamga-stepper > button {
809
+ display: flex;
810
+ flex: 1;
811
+ align-items: center;
812
+ justify-content: center;
813
+ color: var(--color-ink-faint);
814
+ transition:
815
+ background var(--duration-press) var(--ease-standard),
816
+ color var(--duration-press) var(--ease-standard);
817
+ }
818
+ .tamga-stepper > button:first-child {
819
+ border-bottom: 1px solid var(--color-edge);
820
+ }
821
+ .tamga-stepper > button:hover:not(:disabled) {
822
+ background: var(--color-hover);
823
+ color: var(--color-ink);
824
+ }
825
+ .tamga-stepper > button:active:not(:disabled) {
826
+ background: var(--color-sunk);
827
+ }
828
+ .tamga-stepper > button:disabled {
829
+ opacity: 0.4;
830
+ cursor: not-allowed;
831
+ }
832
+
833
+ .tamga-mini-btn {
834
+ border-radius: var(--radius-ctl);
835
+ display: inline-flex;
836
+ align-items: center;
837
+ justify-content: center;
838
+ width: 32px;
839
+ height: 32px;
840
+ border: 1px solid var(--color-edge);
841
+ background: var(--color-shell);
842
+ color: var(--color-ink);
843
+ box-shadow: 1px 1px 0 var(--color-edge);
844
+ transition: transform var(--duration-press) var(--ease-standard), box-shadow var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard),
845
+ border-color var(--duration-press) var(--ease-standard);
846
+ cursor: pointer;
847
+ }
848
+ .tamga-mini-btn:not(:disabled):hover {
849
+ background: var(--color-hover);
850
+ border-color: var(--color-accent-line);
851
+ transform: translate(-1px, -1px);
852
+ box-shadow: 2px 2px 0 var(--color-accent-line);
853
+ }
854
+ .tamga-mini-btn:not(:disabled):active {
855
+ transform: translate(1px, 1px);
856
+ box-shadow: 0 0 0 var(--color-edge);
857
+ }
858
+
859
+ .tamga-input {
860
+ border-radius: var(--radius-ctl);
861
+ height: var(--control);
862
+ width: 100%;
863
+ border: 1px solid var(--color-edge);
864
+ background: var(--color-shell);
865
+ color: var(--color-ink);
866
+ /* Room for a LEADING ICON is opt-in. It used to be baked in at 40px, so every plain field —
867
+ name, title, email — opened with an empty inset that read as a rendering fault. A field
868
+ reserves space for a thing it has only when it has it. */
869
+ padding: 0 16px;
870
+ font-size: var(--text-body);
871
+ outline: none;
872
+ }
873
+ /* Çok satırlı hâli.
874
+
875
+ `.tamga-input` `padding: 0 16px` taşıyor ve tek satır için bu DOĞRU: sabit
876
+ `height` metni zaten dikeyde ortalıyor, dikey padding eklemek onu bozardı.
877
+
878
+ Ama bir `<textarea>` aynı sınıfı alıp yüksekliğini `rows`'tan aldığında o
879
+ sıfır padding görünür bir hataya dönüşüyor: ilk satır üst kenara YAPIŞIYOR.
880
+ Aynı sınıfın iki farklı yükseklik modelinde farklı davranması gerekiyor, ve
881
+ ayrım elemanın kendisinde — o yüzden seçici `textarea`, bir modifier değil.
882
+ Çağıranın hatırlaması gereken bir sınıf olsaydı, unutulduğu her yerde metin
883
+ kenara yapışırdı. */
884
+ textarea.tamga-input {
885
+ height: auto;
886
+ padding-top: 10px;
887
+ padding-bottom: 10px;
888
+ line-height: 1.55;
889
+ resize: vertical;
890
+ }
891
+
892
+ .tamga-input[data-leading="true"] {
893
+ padding-left: 40px;
894
+ }
895
+
896
+ /* Yoğun bir yerdeki alan: bir tablo başlığındaki filtre satırı, bir araç
897
+ çubuğu, bir satır içi düzeltme.
898
+
899
+ NEDEN AYRI BİR ÖLÇÜ DEĞİL. 32px `.tamga-btn-sm` ile AYNI basamak, ve bu
900
+ tesadüf değil: filtre kutusunun yanında duran "Temizle" düğmesi de `sm`
901
+ oluyor, ikisi aynı satırda birbirini tutuyor. Yeni bir yükseklik uydurmak,
902
+ yan yana gelen iki kontrolün 2px kayması demekti.
903
+
904
+ NE ZAMAN KULLANILMAZ. Bir formun kendi alanları. Orada alan ASIL iştir ve
905
+ 40px onun hakkı; küçültmek yalnız yardımcı bir yüzeyde, verinin kendisinden
906
+ daha çok yer kaplamasın diye. */
907
+ .tamga-input-sm {
908
+ height: 32px;
909
+ padding: 0 10px;
910
+ font-size: var(--text-small);
911
+ }
912
+ /* Küçük alanda ikon da içeri geliyor: 40px'lik oyuk 32px'lik bir kutuda
913
+ alanın yarısını yutuyordu. */
914
+ .tamga-input-sm[data-leading="true"] {
915
+ padding-left: 30px;
916
+ }
917
+
918
+ /* `Code`'un dış kutusu.
919
+ *
920
+ * KOPYALAMA DÜĞMESİ MUTLAK KONUMLU ve sağ üste sabitli. Tek satırlık bir
921
+ * gövdede bu bir hataya dönüşüyordu: kutu 42px, düğme 32px, üstte 9px altta
922
+ * 1.5px kalıyordu; düğme ortalı değil ALTA YAPIŞIK görünüyordu.
923
+ *
924
+ * İki kural birlikte çözüyor. `min-height` düğmenin altına ve üstüne eşit
925
+ * boşluk bırakacak kadar yer açıyor (8 + 32 + 8), ve dikey ortalama tek
926
+ * satırlık gövdeyi o yüksekliğin ortasına alıyor, yani metnin ortası ile
927
+ * düğmenin ortası aynı yere düşüyor. Çok satırlı bir gövdede kutu zaten
928
+ * daha uzun: ortalamanın etkisi kalmıyor, düğme sağ üstte kalıyor ve orası
929
+ * bir kod bloğu için doğru yer.
930
+ */
931
+ .tamga-code-block {
932
+ min-height: 48px;
933
+ display: flex;
934
+ flex-direction: column;
935
+ justify-content: center;
936
+ }
937
+
938
+ /*
939
+ * YAZAR METNİ. Bir ürünün açıklaması, bir bilgi tabanı yazısı, bir notun
940
+ * gövdesi: kitin denetlemediği, insanın yazdığı HTML.
941
+ *
942
+ * NEDEN BİR SINIF, BİLEŞEN DEĞİL. Buraya gelen şey zaten bir HTML dizgisi;
943
+ * sarmalayan bir bileşen ona `dangerouslySetInnerHTML`den başka bir şey
944
+ * yapamaz, ve o kararı çağıran vermeli. Kitin katkısı biçim: gelen etiketler
945
+ * kitin kendi ölçeğinde ve kendi renginde çizilsin.
946
+ *
947
+ * NE TEMİZLEMEZ. Bu bir güvenlik sınırı DEĞİL. Hangi etiketin geçeceğine
948
+ * ürün karar veriyor, çünkü beyaz liste ürünün vitrinine ait bir kural.
949
+ *
950
+ * SIFIRLAMA GEREKİYOR ÇÜNKÜ tarayıcının kendi başlık ve liste ölçüleri
951
+ * kitin ölçeğiyle ilgisiz: `h3` 1.17em, `ul` 40px girinti. Ölçek dışından
952
+ * gelen bir sayı, uydurulmuş bir sayı kadar yanlış.
953
+ */
954
+ /*
955
+ * EDİTÖR: araç çubuğu + yazma alanı, tek bir kutu gibi.
956
+ *
957
+ * Çubuk ile alan AYRI ÖĞELER ama TEK bir kenarlık taşıyorlar; iki ayrı
958
+ * kutu, aralarındaki çift çizgiyle "burada iki şey var" der, oysa burada
959
+ * bir tane var.
960
+ *
961
+ * ODAK DIŞ KUTUDA. Yazılan yer içerideki `contenteditable`; odak halkası
962
+ * ona verilirse çubuk halkanın dışında kalıyor ve kontrolün sınırı
963
+ * belirsizleşiyor. `:focus-within` odağı dışarı taşıyor.
964
+ */
965
+ .tamga-editor {
966
+ border: 1px solid var(--color-edge);
967
+ border-radius: var(--radius-ctl);
968
+ /* ZEMİN `--color-shell`, `--color-input` DEĞİL. Ölçüldü ve düzeltildi:
969
+ `--color-input` shadcn geleneğinden gelen bir KENAR rengi (#d2cec8),
970
+ dolgu değil. Onunla boyanan alan girdiden çok DEVRE DIŞI bir kutuya
971
+ benziyordu. Metin kutularının gerçek zemini `--color-shell`, ve editör
972
+ de bir metin kutusu. */
973
+ background: var(--color-shell);
974
+ overflow: hidden;
975
+ }
976
+ .tamga-editor:focus-within {
977
+ outline: 2px solid var(--color-ring);
978
+ outline-offset: -1px;
979
+ }
980
+ .tamga-editor-disabled {
981
+ opacity: 0.6;
982
+ }
983
+
984
+ .tamga-editor-bar {
985
+ display: flex;
986
+ flex-wrap: wrap;
987
+ gap: 2px;
988
+ padding: 4px;
989
+ border-bottom: 1px solid var(--color-line);
990
+ background: var(--color-sunk);
991
+ }
992
+
993
+ /* Biçim düğmesi. AÇIK OLAN DURUM DOLU ZEMİNLE söyleniyor, yalnız renkle
994
+ değil: bir düğmenin basılı olduğunu tek başına renk anlatamaz. */
995
+ .tamga-editor-btn {
996
+ display: inline-flex;
997
+ align-items: center;
998
+ justify-content: center;
999
+ min-width: 28px;
1000
+ height: 28px;
1001
+ padding: 0 6px;
1002
+ border-radius: var(--radius-mark);
1003
+ color: var(--color-ink-soft);
1004
+ font-size: var(--text-caption);
1005
+ cursor: pointer;
1006
+ }
1007
+ .tamga-editor-btn:hover {
1008
+ background: var(--color-hover);
1009
+ color: var(--color-ink);
1010
+ }
1011
+ .tamga-editor-btn[data-on] {
1012
+ background: var(--color-ink);
1013
+ color: var(--color-page);
1014
+ }
1015
+ .tamga-editor-btn:disabled {
1016
+ cursor: default;
1017
+ }
1018
+
1019
+ /* Bağlantı adresi satırı: çubuğun ALTINDA açılıyor, üstünde değil, çünkü
1020
+ çubuk yerinde kalmalı; açılan bir şey düğmelerin yerini oynatırsa
1021
+ kullanıcı ikinci düğmeyi kaybediyor. */
1022
+ .tamga-editor-link {
1023
+ display: flex;
1024
+ align-items: center;
1025
+ gap: 4px;
1026
+ padding: 4px;
1027
+ border-bottom: 1px solid var(--color-line);
1028
+ background: var(--color-sunk);
1029
+ }
1030
+
1031
+ .tamga-editor-body {
1032
+ padding: 12px;
1033
+ outline: none;
1034
+ }
1035
+
1036
+ .tamga-prose {
1037
+ color: var(--color-ink-soft);
1038
+ font-size: var(--text-body);
1039
+ line-height: 1.65;
1040
+ }
1041
+ .tamga-prose > :first-child {
1042
+ margin-top: 0;
1043
+ }
1044
+ .tamga-prose > :last-child {
1045
+ margin-bottom: 0;
1046
+ }
1047
+ .tamga-prose p,
1048
+ .tamga-prose ul,
1049
+ .tamga-prose ol {
1050
+ margin: 0 0 0.75em;
1051
+ }
1052
+ .tamga-prose h3,
1053
+ .tamga-prose h4,
1054
+ .tamga-prose h5 {
1055
+ color: var(--color-ink);
1056
+ font-weight: 600;
1057
+ margin: 1.25em 0 0.5em;
1058
+ }
1059
+ .tamga-prose h3 {
1060
+ font-size: var(--text-subhead);
1061
+ }
1062
+ .tamga-prose h4,
1063
+ .tamga-prose h5 {
1064
+ font-size: var(--text-body);
1065
+ }
1066
+ .tamga-prose ul,
1067
+ .tamga-prose ol {
1068
+ padding-left: 1.25em;
1069
+ }
1070
+ .tamga-prose ul {
1071
+ list-style: disc;
1072
+ }
1073
+ .tamga-prose ol {
1074
+ list-style: decimal;
1075
+ }
1076
+ .tamga-prose li {
1077
+ margin: 0.25em 0;
1078
+ }
1079
+ .tamga-prose strong {
1080
+ color: var(--color-ink);
1081
+ font-weight: 600;
1082
+ }
1083
+ .tamga-prose a {
1084
+ color: var(--color-accent);
1085
+ text-decoration: underline;
1086
+ text-underline-offset: 2px;
1087
+ }
1088
+ .tamga-input::placeholder {
1089
+ color: var(--color-ink-faint);
1090
+ }
1091
+
1092
+ /* An invalid field carries the critical colour on its edge only — never a
1093
+ wash. The message under it is what explains the problem; the field just
1094
+ has to be findable. Paired with aria-invalid by the Input component, so
1095
+ the state is not only visible. */
1096
+ .tamga-input-invalid, .tamga-input-invalid:focus {
1097
+ border-color: var(--color-critical);
1098
+ box-shadow: 2px 2px 0 var(--color-critical);
1099
+ }
1100
+ .tamga-input:focus {
1101
+ border-color: var(--color-accent-line);
1102
+ box-shadow: 2px 2px 0 var(--color-accent-line);
1103
+ outline: none;
1104
+ }
1105
+
1106
+ /* A chip is FLAT, and that is load-bearing rather than shy.
1107
+ In this system depth means "you can press this" — so a raised chip promises
1108
+ a press it cannot deliver. It carried a 2px offset for two days and the
1109
+ promise was the problem: nothing about a status label is clickable.
1110
+ Weight comes from the 1px edge in its own colour instead. Anything that
1111
+ genuinely needs to be pressed stops being a chip and becomes a mini button,
1112
+ which already exists and already lifts. */
1113
+ .tamga-chip {
1114
+ border-radius: var(--radius-ctl);
1115
+ display: inline-flex;
1116
+ align-items: center;
1117
+ gap: 8px;
1118
+ height: 24px;
1119
+ padding: 0 8px;
1120
+ border: 1px solid currentColor;
1121
+ font-size: var(--text-caption);
1122
+ font-weight: 500;
1123
+ }
1124
+ .tamga-chip-mono {
1125
+ font-family: var(--font-mono);
1126
+ font-weight: 700;
1127
+ font-size: var(--text-caption);
1128
+ }
1129
+
1130
+ /* AKTİF ÇİZGİ SEKMENİN KENDİ KENARLIĞI, dışarı taşan bir `::after` değil.
1131
+ *
1132
+ * Önce şöyleydi: `position: absolute; bottom: -1px`, yani işaret sekmenin
1133
+ * kutusunun BİR PİKSEL ALTINA çiziliyordu ve şeridin kendi alt çizgisinin
1134
+ * üstüne oturuyordu. Duran bir şeritte doğru görünüyor.
1135
+ *
1136
+ * Ama şerit KAYDIRILABİLİR olduğu anda kayboluyor. `overflow-x: auto` veren
1137
+ * bir kap, CSS gereği `overflow-y`yi de `auto` yapıyor (bir eksen `visible`
1138
+ * değilse öteki de olamaz), ve kutunun dışına çizilen o bir piksel kırpılıyor.
1139
+ * On dokuz sekmeli bir şeritte aktif sekme işaretsiz kalıyordu: şerit kitin
1140
+ * şeridine "benzemiyordu" ve sebebi buydu.
1141
+ *
1142
+ * Kenarlık kutunun İÇİNDE: kırpılacak bir taşma yok. Saydam bir kenarlık
1143
+ * her sekmede duruyor ki aktif olunca yükseklik değişmesin. */
1144
+ .tamga-tab {
1145
+ display: inline-flex;
1146
+ align-items: center;
1147
+ gap: 8px;
1148
+ height: var(--row-sm);
1149
+ border-bottom: 1px solid transparent;
1150
+ font-size: var(--text-body);
1151
+ font-weight: 500;
1152
+ color: var(--color-ink-faint);
1153
+ cursor: pointer;
1154
+ transition: color var(--duration-press) var(--ease-standard),
1155
+ border-color var(--duration-press) var(--ease-standard);
1156
+ }
1157
+
1158
+ /* Kapalı sekme: yerinde ama gidilemez. Solgun ve tıklanamaz, ama YOK
1159
+ değil, çünkü orada bir şey olduğunu bilmek kullanıcının işine yarıyor. */
1160
+ .tamga-tab[data-disabled] {
1161
+ color: var(--color-ink-faint);
1162
+ cursor: default;
1163
+ pointer-events: none;
1164
+ }
1165
+ .tamga-tab:hover {
1166
+ color: var(--color-ink-soft);
1167
+ }
1168
+ .tamga-tab[data-active="true"] {
1169
+ color: var(--color-ink);
1170
+ border-bottom-color: var(--color-accent-line);
1171
+ }
1172
+
1173
+ /* segmented control: one of N, so it follows the rail — outlined and
1174
+ lifted when chosen, never filled. Filling is reserved for actions. */
1175
+ .tamga-segment {
1176
+ display: inline-flex;
1177
+ align-items: center;
1178
+ gap: 8px;
1179
+ }
1180
+ /* Inactive labels take ink-faint, not nav-idle. nav-idle is an ICON colour —
1181
+ icons clear at 3:1, text needs 4.5:1, and as text it measured 3.29:1 on
1182
+ paper. Same glyph colour, wrong job. */
1183
+ .tamga-segment > button {
1184
+ display: inline-flex;
1185
+ align-items: center;
1186
+ height: var(--control);
1187
+ padding: 0 16px;
1188
+ border: 1px solid transparent;
1189
+ border-radius: var(--radius-ctl);
1190
+ font-size: var(--text-small);
1191
+ font-weight: 500;
1192
+ color: var(--color-ink-faint);
1193
+ cursor: pointer;
1194
+ transition: color var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard), border-color var(--duration-press) var(--ease-standard),
1195
+ box-shadow var(--duration-press) var(--ease-standard), transform var(--duration-press) var(--ease-standard);
1196
+ }
1197
+ .tamga-segment > button:not(:disabled):hover {
1198
+ background: var(--color-nav-hover-bg);
1199
+ color: var(--color-nav-hover);
1200
+ }
1201
+ .tamga-segment > button[data-active="true"] {
1202
+ background: var(--color-shell);
1203
+ color: var(--color-accent-line);
1204
+ border-color: var(--color-accent-line);
1205
+ box-shadow: 2px 2px 0 var(--color-accent-line);
1206
+ }
1207
+ .tamga-segment > button[data-active="true"]:hover {
1208
+ background: var(--color-shell);
1209
+ color: var(--color-accent-line);
1210
+ transform: translate(-1px, -1px);
1211
+ box-shadow: 3px 3px 0 var(--color-accent-line);
1212
+ }
1213
+ .tamga-segment > button[data-active="true"]:active {
1214
+ transform: translate(2px, 2px);
1215
+ box-shadow: 0 0 0 var(--color-accent-line);
1216
+ }
1217
+
1218
+ /* ---------- overlay plane ---------- */
1219
+ /* Same language as everything else — 1px edge, hard offset, no blur.
1220
+ The offset jumps to 6px, past the 4px page maximum, and that gap is
1221
+ what says "this is floating, not part of the page". */
1222
+ .tamga-overlay {
1223
+ border: 1px solid var(--color-edge);
1224
+ border-radius: var(--radius-card);
1225
+ background: var(--color-shell);
1226
+ box-shadow: 6px 6px 0 var(--color-edge);
1227
+ /* KENDİ YÜZEYİ, kendi hizası. Bir panel ağaçta nerede doğduysa oranın
1228
+ hizasını miras alıyordu: bir tablo hücresinin içinde açıldığında
1229
+ (`.tamga-cell-actions` sağa yaslar) panelin İÇİNDEKİ metin de sağa
1230
+ yaslanıyordu. Bir yüzey başladığı yerde metin akışı da yeniden başlar. */
1231
+ text-align: start;
1232
+ }
1233
+ /* TAM EKRAN PANEL: bir çalışma yüzeyi, bir kutu değil.
1234
+ Kenar, yarıçap ve kaydırma gölgesi kalkıyor — üçü de "bu şey sayfanın
1235
+ üstünde duruyor" demek için var, ve sayfanın tamamını kaplayan bir şeyin
1236
+ altında sayfa kalmıyor. Dikey akış: başlık ile ayak yerinde kalırken gövde
1237
+ kendi içinde kayıyor. */
1238
+ .tamga-overlay-full {
1239
+ display: flex;
1240
+ height: 100%;
1241
+ flex-direction: column;
1242
+ border: 0;
1243
+ border-radius: 0;
1244
+ box-shadow: none;
1245
+ }
1246
+ /* ---------- steps ---------- */
1247
+ /*
1248
+ * ADIM ŞERİDİ, ve üç durumu üç FARKLI FİZİKLE söylüyor.
1249
+ *
1250
+ * Önce üçü de aynı şeydi: ince kenarlı bir daire, yalnız rengi değişiyordu.
1251
+ * Renk tek başına bir hiyerarşi kurmuyor — ve daire, kitin hiçbir yerde
1252
+ * kullanmadığı bir şekil. Kit tam yuvarlağı yalnız avatarda ve noktada
1253
+ * kullanıyor; bir adım işareti bir kontrol boyunda bir işarettir, o yüzden
1254
+ * kontrolün yarıçapını alıyor.
1255
+ *
1256
+ * ÜÇ DURUM, ÜÇ FİZİK (13 Yasa 1: 1px kenar + kaydırılmış tam renk):
1257
+ * gelecek düz durur — henüz bir şey olmadı
1258
+ * şimdiki YÜKSELİR — 2px sert kaydırma, aksan renginde: buradasın
1259
+ * biten OTURUR — dolu aksan, kaydırma yok: bitmiş bir iş kalkmaz
1260
+ *
1261
+ * ÇİZGİ DE BİLGİ TAŞIYOR. Geçilen aralık aksan, kalanı kenar rengi: şerit
1262
+ * yalnız adımları saymıyor, ne kadar yol alındığını da gösteriyor. 1px
1263
+ * `--color-line` idi ve kâğıdın üstünde neredeyse görünmüyordu.
1264
+ */
1265
+ .tamga-step-mark {
1266
+ display: inline-flex;
1267
+ width: var(--step-mark);
1268
+ height: var(--step-mark);
1269
+ flex-shrink: 0;
1270
+ align-items: center;
1271
+ justify-content: center;
1272
+ border: 1px solid var(--color-edge);
1273
+ border-radius: var(--radius-ctl);
1274
+ background: var(--color-shell);
1275
+ color: var(--color-ink-faint);
1276
+ font-variant-numeric: tabular-nums;
1277
+ transition:
1278
+ box-shadow var(--duration-quick) var(--ease-standard),
1279
+ background var(--duration-quick) var(--ease-standard);
1280
+ }
1281
+ .tamga-step-mark[data-state="current"] {
1282
+ border-color: var(--color-accent-line);
1283
+ color: var(--color-accent-line);
1284
+ box-shadow: 2px 2px 0 0 var(--color-accent-line);
1285
+ }
1286
+ .tamga-step-mark[data-state="done"] {
1287
+ border-color: var(--color-accent-line);
1288
+ background: var(--color-accent-line);
1289
+ color: var(--color-accent-ink);
1290
+ }
1291
+ .tamga-step-line {
1292
+ display: block;
1293
+ width: var(--step-line);
1294
+ height: var(--mark-tick);
1295
+ flex-shrink: 0;
1296
+ background: var(--color-edge);
1297
+ }
1298
+ .tamga-step-line[data-done="true"] {
1299
+ background: var(--color-accent-line);
1300
+ }
1301
+
1302
+ .tamga-scrim {
1303
+ background: var(--color-scrim);
1304
+ }
1305
+
1306
+ /* ---------- form controls ---------- */
1307
+ /* checkbox and radio carry the same 1px edge and 1px offset as a mini
1308
+ button, so they read as part of the family rather than browser chrome */
1309
+ /*
1310
+ * SEÇENEK İŞARETİ İLK SATIRIN ORTASINA.
1311
+ *
1312
+ * Etiket tek satırken kutuyu dikeyde ortalamak doğru duruyor; altına bir
1313
+ * açıklama satırı eklenince işaret İKİ SATIRIN ortasına kaçıyor ve neyi
1314
+ * işaretlediği belirsizleşiyor. İşaret ilk satıra ait.
1315
+ *
1316
+ * Sayı UYDURULMUYOR, satırdan hesaplanıyor: `1lh` öğenin kendi satır
1317
+ * kutusu, `18px` de işaretin boyu (yukarıdaki kuralın kendisi), ve fark
1318
+ * ikiye bölününce işaret ilk satırın tam ortasına oturuyor. Punto ya da
1319
+ * satır aralığı değişirse hizalama kendiliğinden düzeliyor.
1320
+ */
1321
+ .tamga-choice-mark {
1322
+ align-self: flex-start;
1323
+ margin-top: calc((1lh - 18px) / 2);
1324
+ }
1325
+
1326
+ .tamga-check, .tamga-radio {
1327
+ display: inline-flex;
1328
+ align-items: center;
1329
+ justify-content: center;
1330
+ width: 18px;
1331
+ height: 18px;
1332
+ flex-shrink: 0;
1333
+ border: 1px solid var(--color-edge);
1334
+ border-radius: 3px;
1335
+ background: var(--color-shell);
1336
+ color: var(--color-accent-ink);
1337
+ box-shadow: 1px 1px 0 var(--color-edge);
1338
+ cursor: pointer;
1339
+ transition: background var(--duration-press) var(--ease-standard), border-color var(--duration-press) var(--ease-standard), box-shadow var(--duration-press) var(--ease-standard);
1340
+ }
1341
+ .tamga-check:hover, .tamga-radio:hover {
1342
+ border-color: var(--color-accent-line);
1343
+ box-shadow: 1px 1px 0 var(--color-accent-line);
1344
+ }
1345
+ .tamga-check[data-checked="true"] {
1346
+ background: var(--color-accent);
1347
+ border-color: var(--color-accent);
1348
+ box-shadow: 1px 1px 0 var(--color-accent-shadow);
1349
+ }
1350
+ .tamga-radio[data-checked="true"] {
1351
+ border-color: var(--color-accent);
1352
+ box-shadow: 1px 1px 0 var(--color-accent);
1353
+ }
1354
+ .tamga-radio[data-checked="true"]::before {
1355
+ content: "";
1356
+ width: 8px;
1357
+ height: 8px;
1358
+ border-radius: 1px;
1359
+ background: var(--color-accent);
1360
+ }
1361
+
1362
+ .tamga-switch {
1363
+ display: inline-flex;
1364
+ align-items: center;
1365
+ width: 40px;
1366
+ height: 24px;
1367
+ flex-shrink: 0;
1368
+ padding: 2px;
1369
+ border: 1px solid var(--color-edge);
1370
+ border-radius: var(--radius-ctl);
1371
+ background: var(--color-shell);
1372
+ cursor: pointer;
1373
+ transition: background var(--duration-press) var(--ease-standard), border-color var(--duration-press) var(--ease-standard);
1374
+ }
1375
+ .tamga-switch[data-on="true"] {
1376
+ background: var(--color-accent);
1377
+ border-color: var(--color-accent);
1378
+ }
1379
+ .tamga-switch > span {
1380
+ display: block;
1381
+ width: 16px;
1382
+ height: 16px;
1383
+ border-radius: 2px;
1384
+ background: var(--color-edge);
1385
+ transition: transform var(--duration-press) var(--ease-standard), background var(--duration-press) var(--ease-standard);
1386
+ }
1387
+ .tamga-switch[data-on="true"] > span {
1388
+ background: var(--color-accent-ink);
1389
+ transform: translateX(16px);
1390
+ }
1391
+
1392
+
1393
+ /* A skeleton breathes; it never shimmers. A sweeping gradient is the
1394
+ default-SaaS tell and this product has no gradients anywhere else, so one
1395
+ here would be the single exception — and it would be the loudest thing on
1396
+ a page whose whole point is that nothing has arrived yet.
1397
+ Two blocks, not one: the ground never animates, only the bar on top of it.
1398
+ That keeps a floor under the shape so it stays a placeholder at every point
1399
+ in the cycle instead of fading toward the page and back. */
1400
+ /* Progress is an instrument scale, not a bar. The ticks let the value be read
1401
+ without reading the number, which is what a gauge is for.
1402
+ Deliberately NOT the segmented strip form: a segmented horizontal strip is a
1403
+ timeline — one mark per bucket, colour as state — and reusing that shape for
1404
+ completion would read as history rather than as progress.
1405
+ Neutral throughout — progress reports completion, never health, so a status
1406
+ colour here would be a false signal.
1407
+ Note it carries a 2px offset, so it paints outside its box: reserve room in
1408
+ the container or the offset lands on whatever sits below. */
1409
+ .tamga-progress {
1410
+ position: relative;
1411
+ height: 16px;
1412
+ background: var(--color-sunk);
1413
+ border: 1px solid var(--color-edge);
1414
+ box-shadow: 2px 2px 0 var(--color-edge);
1415
+ border-radius: var(--radius-ctl);
1416
+ overflow: hidden;
1417
+ }
1418
+ .tamga-progress-fill {
1419
+ height: 100%;
1420
+ background: var(--color-ink);
1421
+ transition: width var(--duration-base) var(--ease-standard);
1422
+ }
1423
+ .tamga-progress-tick {
1424
+ position: absolute;
1425
+ top: 0;
1426
+ height: 100%;
1427
+ width: 1px;
1428
+ }
1429
+
1430
+ .tamga-skeleton {
1431
+ position: relative;
1432
+ background: var(--color-sunk);
1433
+ border-radius: 3px;
1434
+ overflow: hidden;
1435
+ }
1436
+ .tamga-skeleton::after {
1437
+ content: "";
1438
+ position: absolute;
1439
+ inset: 0;
1440
+ background: var(--color-edge);
1441
+ opacity: 0.78;
1442
+ animation: tamga-breathe var(--duration-breath) var(--ease-breath) infinite;
1443
+ animation-delay: var(--tamga-delay, 0ms);
1444
+ }
1445
+
1446
+ .tamga-option {
1447
+ display: flex;
1448
+ width: 100%;
1449
+ align-items: center;
1450
+ gap: 8px;
1451
+ height: 40px;
1452
+ padding: 0 16px;
1453
+ font-size: var(--text-body);
1454
+ text-align: left;
1455
+ color: var(--color-ink-soft);
1456
+ cursor: pointer;
1457
+ }
1458
+ /* KLAVYE VURGUSU FARE HOVER'IYLA AYNI GÖRÜNÜR.
1459
+ İki ayrı görünüş vermek, aynı anlamı ("Enter buna basacak") iki dille
1460
+ anlatmak olurdu; kullanıcının ikisini de öğrenmesi gerekirdi. */
1461
+ .tamga-option:hover,
1462
+ .tamga-option[data-active] {
1463
+ background: var(--color-hover);
1464
+ color: var(--color-ink);
1465
+ }
1466
+ .tamga-option[data-selected="true"] {
1467
+ background: var(--color-accent-bg);
1468
+ color: var(--color-accent-line);
1469
+ box-shadow: inset 2px 0 0 var(--color-accent-line);
1470
+ }
1471
+
1472
+ /* ---------- feedback and structure ---------- */
1473
+
1474
+ /* inline banner — status wash, no fill, a left rule carries the state */
1475
+ .tamga-alert {
1476
+ display: flex;
1477
+ gap: 16px;
1478
+ padding: 16px;
1479
+ border: 1px solid var(--color-edge);
1480
+ border-left-width: 3px;
1481
+ border-radius: var(--radius-ctl);
1482
+ background: var(--color-shell);
1483
+ }
1484
+
1485
+ /* the live mark — neutral by rule: running is not a deviation */
1486
+ .tamga-beacon {
1487
+ display: inline-block;
1488
+ width: 8px;
1489
+ height: 8px;
1490
+ flex-shrink: 0;
1491
+ background: var(--color-silent);
1492
+ }
1493
+ .tamga-beacon[data-live="true"] {
1494
+ animation: tamga-pulse var(--duration-beacon) var(--ease-breath) infinite;
1495
+ }
1496
+
1497
+ /* keeps a button's width when its label swaps for a loading label */
1498
+ .tamga-swap {
1499
+ display: grid;
1500
+ place-items: center;
1501
+ }
1502
+ .tamga-swap > * {
1503
+ grid-area: 1 / 1;
1504
+ display: inline-flex;
1505
+ align-items: center;
1506
+ justify-content: center;
1507
+ gap: 8px;
1508
+ white-space: nowrap;
1509
+ }
1510
+ .tamga-swap > [data-hidden="true"] {
1511
+ visibility: hidden;
1512
+ }
1513
+
1514
+
1515
+ .tamga-rule-h {
1516
+ height: 1px;
1517
+ width: 100%;
1518
+ background: var(--color-line);
1519
+ }
1520
+ .tamga-rule-v {
1521
+ width: 1px;
1522
+ align-self: stretch;
1523
+ background: var(--color-line);
1524
+ }
1525
+
1526
+ /* ---------- lists and tables ---------- */
1527
+ /* row height is fixed so a list never breathes with its content */
1528
+
1529
+ /* Slider topuzu.
1530
+
1531
+ `appearance: none` çubuğu bize bırakıyor ama TOPUZU DA SİLİYOR — ve
1532
+ silinen topuz hata vermiyor, sadece kaydırıcı tutulamaz hâle geliyor.
1533
+ WebKit ve Firefox iki ayrı sözde-eleman kullanıyor; ikisi de yazılmak
1534
+ zorunda, biri unutulursa o tarayıcıda topuz yok. */
1535
+ .tamga-slider::-webkit-slider-thumb {
1536
+ appearance: none;
1537
+ width: 16px;
1538
+ height: 16px;
1539
+ border: 1px solid var(--color-accent-line);
1540
+ border-radius: 50%;
1541
+ background: var(--color-shell);
1542
+ box-shadow: 1px 1px 0 var(--color-accent-line);
1543
+ cursor: grab;
1544
+ }
1545
+ .tamga-slider::-moz-range-thumb {
1546
+ width: 16px;
1547
+ height: 16px;
1548
+ border: 1px solid var(--color-accent-line);
1549
+ border-radius: 50%;
1550
+ background: var(--color-shell);
1551
+ box-shadow: 1px 1px 0 var(--color-accent-line);
1552
+ cursor: grab;
1553
+ }
1554
+ .tamga-slider:focus-visible {
1555
+ outline: 2px solid var(--color-accent-line);
1556
+ outline-offset: 4px;
1557
+ }
1558
+
1559
+ /* THE GUTTER IS THE ROW'S, not the caller's.
1560
+ *
1561
+ * This class carried no horizontal padding, and every single caller added
1562
+ * `tamga-gutter` by hand: the settings sub-nav, both skeletons, the docs
1563
+ * example. Five out of five is not a convention, it is a missing rule, and
1564
+ * the sixth caller (a blocks recipe) forgot it and rendered edge to edge.
1565
+ *
1566
+ * `.tamga-table` was already doing this correctly through
1567
+ * `td:first-child { padding-left: var(--gutter) }`. A row and a cell sit on
1568
+ * the same left rule; one of them should not need reminding. */
1569
+ .tamga-list-row {
1570
+ display: flex;
1571
+ align-items: center;
1572
+ gap: 16px;
1573
+ height: var(--row);
1574
+ padding-inline: var(--gutter);
1575
+ border-bottom: 1px solid var(--color-line);
1576
+ transition: background var(--duration-press) var(--ease-standard);
1577
+ }
1578
+ .tamga-list-row:last-child {
1579
+ border-bottom: none;
1580
+ }
1581
+ .tamga-list-row:hover {
1582
+ background: var(--color-sunk);
1583
+ }
1584
+ .tamga-list-row-sm {
1585
+ height: var(--row-sm);
1586
+ }
1587
+
1588
+ .tamga-table {
1589
+ width: 100%;
1590
+ border-collapse: collapse;
1591
+ text-align: left;
1592
+ }
1593
+ .tamga-table th, .tamga-table td {
1594
+ padding: 0 16px;
1595
+ overflow: hidden;
1596
+ white-space: nowrap;
1597
+ text-overflow: ellipsis;
1598
+ }
1599
+ /* A cell that HOSTS AN OVERLAY cannot clip its own content.
1600
+ The truncation trio above (overflow:hidden + nowrap + ellipsis) is right for text and fatal for
1601
+ a row's action menu: the panel is absolutely positioned inside the cell, so `overflow:hidden`
1602
+ cuts it off at the cell's edge. Measured 2026-08-20 — the menu rendered, looked fine, and
1603
+ swallowed every click, because `elementFromPoint` returned the td.
1604
+ Actions never need truncating, so the cell opts out of all three rather than the table losing
1605
+ ellipsis everywhere.
1606
+
1607
+ AND IT REALLY IS ALL THREE. The comment said three, the rule reset two: `white-space: nowrap`
1608
+ stayed, and it is inherited, so every panel opened inside this cell inherited it too. A popover
1609
+ listing an order's products rendered each line on one unbreakable line and spilled past its own
1610
+ edge. The overlay looked broken; the cause was a table rule three levels up. Found 2026-09-09
1611
+ in a consumer panel.
1612
+
1613
+ KIRPMA VE HİZA AYRI İKİ ŞEY. Bu kaçış uzun süre tek parçaydı ve sağa
1614
+ yaslamayı da beraberinde getiriyordu, yani sola yaslı bir hücrede bir ipucu
1615
+ ya da açılır panel barındırmak isteyen, hizasını da feda etmek zorunda
1616
+ kalıyordu. Yakalandığı yer: bir tüketici listesinde durum rozetinin ipucu
1617
+ hücre kenarında kesiliyordu, ve tek çare hücreyi sağa yaslamaktı.
1618
+
1619
+ SEÇİCİ `.tamga-table td` KADAR SPESİFİK OLMAK ZORUNDA: düz
1620
+ `.tamga-cell-open` (0,1,0) yazılırsa kırpma kuralı `.tamga-table td`
1621
+ (0,1,1) onu yener ve sınıf hiçbir şey yapmaz. */
1622
+ .tamga-table td.tamga-cell-open,
1623
+ .tamga-table th.tamga-cell-open {
1624
+ overflow: visible;
1625
+ white-space: normal;
1626
+ text-overflow: clip;
1627
+ }
1628
+ .tamga-table td.tamga-cell-actions {
1629
+ text-align: right;
1630
+ }
1631
+
1632
+ .tamga-table th:first-child, .tamga-table td:first-child {
1633
+ padding-left: var(--gutter);
1634
+ }
1635
+ .tamga-table th:last-child, .tamga-table td:last-child {
1636
+ padding-right: var(--gutter);
1637
+ }
1638
+ .tamga-table thead th {
1639
+ height: var(--row-sm);
1640
+ border-bottom: 1px solid var(--color-line);
1641
+ font-size: var(--text-caption);
1642
+ font-weight: 500;
1643
+ color: var(--color-ink-faint);
1644
+ }
1645
+ .tamga-table tbody tr {
1646
+ height: var(--row);
1647
+ border-bottom: 1px solid var(--color-line);
1648
+ transition: background var(--duration-press) var(--ease-standard);
1649
+ }
1650
+ .tamga-table tbody tr:last-child {
1651
+ border-bottom: none;
1652
+ }
1653
+ .tamga-table tbody tr:hover {
1654
+ background: var(--color-sunk);
1655
+ }
1656
+ .tamga-table tbody tr[data-selected="true"] {
1657
+ background: var(--color-accent-bg);
1658
+ }
1659
+ .tamga-table tbody tr[data-selected="true"] td:first-child {
1660
+ box-shadow: inset 2px 0 0 var(--color-accent-line);
1661
+ }
1662
+ .tamga-list-row[data-selected="true"] {
1663
+ background: var(--color-accent-bg);
1664
+ box-shadow: inset 2px 0 0 var(--color-accent-line);
1665
+ }
1666
+
1667
+ .tamga-link {
1668
+ color: var(--color-accent-line);
1669
+ font-weight: 500;
1670
+ text-underline-offset: 3px;
1671
+ }
1672
+ .tamga-link:hover {
1673
+ text-decoration: underline;
1674
+ }
1675
+
1676
+ /* ------------------------------------------------------------------ *
1677
+ * KPI KAROSU: tek bir sayı, büyük.
1678
+ *
1679
+ * REFERANS NOKTASI YUMUŞAK KARTTI (daire ikon rozeti, bulanık gölge, kenar
1680
+ * yok); buradaki karşılığı 1. yasa: 1px kenar, BULANIKSIZ kaymış gölge, ve
1681
+ * daire yerine KARE karo. Kit hiçbir yerde daire kullanmıyor (avatar ve
1682
+ * nokta dışında) ve tek bir daire, kartı ödünç alınmış gösteriyor.
1683
+ *
1684
+ * İKON KARONUN ZEMİNİ `--color-hover`, marka rengi DEĞİL. Bir panoda dört
1685
+ * karo yan yana duruyor; dördünün de ikonu marka renginde parlarsa hiçbiri
1686
+ * öne çıkmıyor ve göz asıl bakması gereken sayıyı en son buluyor. Renk
1687
+ * kartın kendi verisinin (`accent` şeridi) ve dikkat isteyen sayının hakkı.
1688
+ * ------------------------------------------------------------------ */
1689
+ .tamga-kpi {
1690
+ position: relative;
1691
+ display: flex;
1692
+ align-items: flex-start;
1693
+ gap: 12px;
1694
+ padding: 16px;
1695
+ border-radius: var(--radius-card);
1696
+ border: 1px solid var(--color-edge);
1697
+ background: var(--color-shell);
1698
+ box-shadow: 2px 2px 0 var(--color-edge);
1699
+ overflow: hidden;
1700
+ text-align: left;
1701
+ text-decoration: none;
1702
+ }
1703
+
1704
+ .tamga-kpi-tile {
1705
+ display: flex;
1706
+ align-items: center;
1707
+ justify-content: center;
1708
+ flex-shrink: 0;
1709
+ width: 40px;
1710
+ height: 40px;
1711
+ border-radius: var(--radius-ctl);
1712
+ border: 1px solid var(--color-line);
1713
+ background: var(--color-hover);
1714
+ color: var(--color-ink-soft);
1715
+ }
1716
+
1717
+ /*
1718
+ * FİZİK YALNIZ TIKLANABİLİR KARODA.
1719
+ *
1720
+ * `.tamga-kpi-live` bir stil değil bir SÖZ: bu karo tıklanıyor. Okunan bir
1721
+ * karo hover'da yükselirse tıklanabilir göründüğü hâlde hiçbir şey yapmıyor,
1722
+ * ve kullanıcı bir kez tıklayıp bir daha denemiyor.
1723
+ *
1724
+ * Kaymalar 1. yasanın kendisi: hover'da -1/-1 ve 3px gölge, basınca +2/+2 ve
1725
+ * gölge yok. `--offset-room` tuzağı burada da geçerli — bu karolar bir
1726
+ * ızgarada duruyor ve ızgaranın kendisi kırpmıyor, ama kaydırılan bir kabın
1727
+ * içine konulursa `.tamga-rail-scroll` gibi bir dolgu gerekir.
1728
+ */
1729
+ .tamga-kpi-live {
1730
+ cursor: pointer;
1731
+ transition:
1732
+ transform var(--duration-press, 120ms) var(--ease-press, ease-out),
1733
+ box-shadow var(--duration-press, 120ms) var(--ease-press, ease-out),
1734
+ background var(--duration-press, 120ms);
1735
+ }
1736
+ /* HOVER'DA KENAR RENGİ DEĞİŞMİYOR, ve ilk sürümde değişiyordu. Kitin
1737
+ 1. yasasında hover üç şey yapar: zemin bir tık koyulaşır, kontrol
1738
+ -1/-1 yükselir, gölge 3px'e çıkar. Kenarın renklenmesi bu dilde yok
1739
+ (`tamga-btn`de de, `tamga-icon-btn`de de yok) ve tek başına bir kartta
1740
+ yapıldığında kart ötekilerden farklı bir şey konuşuyor. */
1741
+ .tamga-kpi-live:hover {
1742
+ background: var(--color-hover);
1743
+ transform: translate(-1px, -1px);
1744
+ box-shadow: 3px 3px 0 var(--color-edge);
1745
+ }
1746
+ .tamga-kpi-live:active {
1747
+ transform: translate(2px, 2px);
1748
+ box-shadow: 0 0 0 var(--color-edge);
1749
+ }
1750
+
1751
+ /* SEÇİLİ HÂL NÖTR, VURGU RENGİ DEĞİL. Vurgu rengi bir EYLEMİN rengi (kaydet,
1752
+ sil); bir filtrenin açık olması bir eylem değil bir DURUM. Vurguya
1753
+ boyandığında listede bir sorun varmış gibi okunuyordu. */
1754
+ .tamga-kpi-live[data-pressed] {
1755
+ border-color: var(--color-ink);
1756
+ background: var(--color-hover);
1757
+ box-shadow: 2px 2px 0 var(--color-ink);
1758
+ }
1759
+ .tamga-kpi-live[data-pressed]:hover {
1760
+ box-shadow: 3px 3px 0 var(--color-ink);
1761
+ }
1762
+ .tamga-kpi-live[data-pressed]:active {
1763
+ box-shadow: 0 0 0 var(--color-ink);
1764
+ }
1765
+
1766
+ /*
1767
+ * KAROLARIN IZGARASI.
1768
+ *
1769
+ * SÜTUN SAYISI KARO SAYISINDAN geliyor (`--tamga-kpi-cols`), ekrandan
1770
+ * verilmiyor. Elle verilen bir sütun sayısı yeni bir karo eklendiğinde
1771
+ * yalnız o ekranda güncelleniyor, ve tek başına kalan bir karo bütün satırı
1772
+ * kaplayıp panoyu dengesiz gösteriyor.
1773
+ *
1774
+ * Dar ekranda iki sütun, geniş ekranda karo sayısı kadar: bir karo 40px
1775
+ * ikon ve display boyunda bir sayı taşıyor, üçten dar bir sütunda sayı
1776
+ * sarıyor.
1777
+ */
1778
+ .tamga-kpi-grid {
1779
+ display: grid;
1780
+ gap: 16px;
1781
+ grid-template-columns: minmax(0, 1fr);
1782
+ }
1783
+ @media (min-width: 640px) {
1784
+ .tamga-kpi-grid {
1785
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1786
+ }
1787
+ }
1788
+ @media (min-width: 1280px) {
1789
+ .tamga-kpi-grid {
1790
+ grid-template-columns: repeat(var(--tamga-kpi-cols, 4), minmax(0, 1fr));
1791
+ }
1792
+ }
1793
+
1794
+ .tamga-label {
1795
+ font-size: var(--text-caption);
1796
+ font-weight: 500;
1797
+ color: var(--color-ink-faint);
1798
+ }
1799
+
1800
+ /* an identifier-shaped micro-label above a title: mono, wide, quiet. It says
1801
+ what KIND of thing this is before the title says which one. */
1802
+ .tamga-kicker {
1803
+ font-family: var(--font-mono);
1804
+ font-size: var(--text-micro);
1805
+ font-weight: 500;
1806
+ letter-spacing: 0.09em;
1807
+ text-transform: uppercase;
1808
+ color: var(--color-ink-faint);
1809
+ }
1810
+
1811
+ /* ---------- the mascot's furniture ---------- */
1812
+ /* The mascot lives INSIDE the instrument, never floating on top of it.
1813
+ Everything here exists to say that in the language the rest of the kit
1814
+ already speaks: a seated recess, ruled paper, a hard floor, a hard
1815
+ shadow. Drop the art on a plain coloured plane and you get somebody
1816
+ else's product with our artwork pasted into it. */
1817
+
1818
+ /* The well: a recess carved into the panel. SEATED physics — sunk ground,
1819
+ no offset, never lifts. The ground defaults to --color-sunk; a call site
1820
+ may pass --art-ground to tint it. */
1821
+ .tamga-art-well {
1822
+ position: relative;
1823
+ background: var(--art-ground, var(--color-sunk));
1824
+ overflow: hidden;
1825
+ }
1826
+ /* Ruled paper. The art direction is "an instrument on paper", so the ground
1827
+ the figure stands on is squared paper — the same 8px module the layout uses.
1828
+ It also does real work: the grid gives the eye a scale, which is what
1829
+ stops a large drawing from reading as a decorative blob. */
1830
+ .tamga-art-well::before {
1831
+ content: "";
1832
+ position: absolute;
1833
+ inset: 0;
1834
+ background-image: linear-gradient(to right, var(--color-line) 1px, transparent 1px),
1835
+ linear-gradient(to bottom, var(--color-line) 1px, transparent 1px);
1836
+ background-size: 8px 8px;
1837
+ opacity: 0.7;
1838
+ pointer-events: none;
1839
+ }
1840
+ /* content sits above the ruling */
1841
+ .tamga-art-well > * {
1842
+ position: relative;
1843
+ }
1844
+
1845
+ /* The floor. A character standing on a hard rule reads as standing; the same
1846
+ character with nothing under it reads as falling. One line fixes it. */
1847
+ .tamga-art-floor {
1848
+ border-bottom: 1px solid var(--color-edge);
1849
+ }
1850
+
1851
+ /* The cast shadow: the elevation formula applied to a character. Our light
1852
+ has always come from the upper left — every offset in the kit lands down
1853
+ and to the right — so the figure's shadow does too. A BAR rather than a copy of
1854
+ his silhouette, deliberately: a body-shaped offset would read as the
1855
+ pressable-object signature, and artwork is not pressable (Yasa: depth means
1856
+ pressable). A bar reads as ground, not as affordance. */
1857
+ .tamga-art-fig {
1858
+ position: relative;
1859
+ display: inline-block;
1860
+ }
1861
+ /* Off at portrait size: a 3px bar under 30px of art is noise, not ground. */
1862
+ .tamga-art-fig[data-ground="true"]::after {
1863
+ content: "";
1864
+ position: absolute;
1865
+ left: 26%;
1866
+ right: -4px;
1867
+ bottom: 0;
1868
+ height: 3px;
1869
+ background: var(--color-edge);
1870
+ pointer-events: none;
1871
+ }
1872
+
1873
+ /* The portrait: the figure framed in a window, for use inside a row.
1874
+ 72px, not 46px. Every drawing is a SCENE — a figure at a desk, a figure in a
1875
+ boat — and a scene under about 50px is a smudge: the frame reads, the
1876
+ character does not, so the whole thing becomes decoration that costs a row.
1877
+ If it cannot be seen it should not be there. */
1878
+ .tamga-art-port {
1879
+ display: flex;
1880
+ align-items: flex-end;
1881
+ justify-content: center;
1882
+ flex-shrink: 0;
1883
+ width: 72px;
1884
+ height: 72px;
1885
+ padding-top: 6px;
1886
+ border: 1px solid var(--color-edge);
1887
+ border-radius: var(--radius-sm);
1888
+ }
1889
+
1890
+ /* The base plate: what happens next, as an instrument's legend.
1891
+ A whole-surface empty state that is one short paragraph and two buttons
1892
+ leaves the reader with nothing to do but read the buttons. The steps are
1893
+ the actual answer to "what IS this screen for" — and they are what earns
1894
+ the full width, instead of stretching two sentences across it.
1895
+ Seated: sunk ground, no offset, so it never competes with the action. */
1896
+ .tamga-art-steps {
1897
+ display: grid;
1898
+ border-top: 1px solid var(--color-edge);
1899
+ }
1900
+ .tamga-art-step {
1901
+ display: flex;
1902
+ align-items: baseline;
1903
+ gap: 10px;
1904
+ padding: 14px 20px;
1905
+ border-top: 1px solid var(--color-line);
1906
+ }
1907
+ .tamga-art-step:first-child {
1908
+ border-top: 0;
1909
+ }
1910
+ @media (width >= 40rem) {
1911
+ .tamga-art-steps {
1912
+ grid-auto-flow: column;
1913
+ grid-auto-columns: 1fr;
1914
+ }
1915
+ .tamga-art-step {
1916
+ border-top: 0;
1917
+ border-left: 1px solid var(--color-line);
1918
+ padding: 16px 24px;
1919
+ }
1920
+ .tamga-art-step:first-child {
1921
+ border-left: 0;
1922
+ }
1923
+ }
1924
+ /* mono, because it is a SEQUENCE and not a quantity */
1925
+ .tamga-art-step-n {
1926
+ flex-shrink: 0;
1927
+ font-family: var(--font-mono);
1928
+ font-size: var(--text-caption);
1929
+ font-weight: 600;
1930
+ color: var(--color-accent-line);
1931
+ }
1932
+
1933
+ /* The stage: the empty state is ONE object made of two materials — a seated
1934
+ well on the left, the raised face on the right, divided by a real edge.
1935
+ Both physics visible at once, which is the whole idea of the kit. */
1936
+ .tamga-art-stage {
1937
+ display: flex;
1938
+ align-items: flex-end;
1939
+ justify-content: center;
1940
+ flex-shrink: 0;
1941
+ min-width: 236px;
1942
+ padding: 24px 28px 0;
1943
+ border-right: 1px solid var(--color-edge);
1944
+ }
1945
+
1946
+ /* A full-width shallow band of ruled paper with a floor under it — the
1947
+ mascot's stage when the composition runs on ONE vertical axis rather than
1948
+ splitting left and right. */
1949
+ .tamga-art-band {
1950
+ display: flex;
1951
+ align-items: flex-end;
1952
+ justify-content: center;
1953
+ width: 100%;
1954
+ padding-top: 28px;
1955
+ border-bottom: 1px solid var(--color-edge);
1956
+ }
1957
+
1958
+ /* The ticket's divider. It was drawn as a PERFORATION — a dashed rule meaning
1959
+ "tear here" — and v5 proved that wrong on contact: dashed already means
1960
+ "not real content yet" (the logo bounding box,
1961
+ the logo bounding box). A second meaning for the same mark makes a finished
1962
+ empty state read as a placeholder, which is the opposite of what it says.
1963
+ Solid, in the edge colour, like every other divider. Changed here too so
1964
+ the two do not drift — a kit whose copy disagrees with it is not a kit. */
1965
+ .tamga-art-divider {
1966
+ height: 0;
1967
+ border-top: 1px solid var(--color-edge);
1968
+ }
1969
+
1970
+ /* A way in, offered as a row rather than a button. An empty screen with two
1971
+ buttons asks a yes/no question; a list of routes answers "what can I even
1972
+ do here", which is the actual question somebody arriving has. */
1973
+ .tamga-art-route {
1974
+ display: flex;
1975
+ width: 100%;
1976
+ align-items: center;
1977
+ gap: 12px;
1978
+ padding: 12px 14px;
1979
+ text-align: left;
1980
+ border: 1px solid var(--color-edge);
1981
+ border-radius: var(--radius-ctl);
1982
+ background: var(--color-shell);
1983
+ box-shadow: 1px 1px 0 var(--color-edge);
1984
+ color: var(--color-ink);
1985
+ cursor: pointer;
1986
+ transition: transform var(--duration-press) var(--ease-standard),
1987
+ box-shadow var(--duration-press) var(--ease-standard),
1988
+ border-color var(--duration-press) var(--ease-standard);
1989
+ }
1990
+ .tamga-art-route:hover {
1991
+ border-color: var(--color-accent-line);
1992
+ transform: translate(-1px, -1px);
1993
+ box-shadow: 2px 2px 0 var(--color-accent-line);
1994
+ }
1995
+ .tamga-art-route:active {
1996
+ transform: translate(1px, 1px);
1997
+ box-shadow: 0 0 0 var(--color-edge);
1998
+ }
1999
+
2000
+ /* A logo tile. The mascot's portrait window is 72px because a SCENE needs the room; a service
2001
+ mark is a single glyph and reads at 48. Same frame, different job — sharing the portrait class
2002
+ made every integration card look like it was missing something. */
2003
+ .tamga-logo-tile {
2004
+ display: flex;
2005
+ align-items: center;
2006
+ justify-content: center;
2007
+ flex-shrink: 0;
2008
+ width: 48px;
2009
+ height: 48px;
2010
+ border: 1px solid var(--color-edge);
2011
+ border-radius: var(--radius-sm);
2012
+ background: var(--color-sunk);
2013
+ }
2014
+ .tamga-logo-tile-sm {
2015
+ width: 40px;
2016
+ height: 40px;
2017
+ }
2018
+
2019
+ /* No box at all. For a whole body with nothing in it, where a bordered panel
2020
+ would be a box drawn around a void — the floor rule is the only structure,
2021
+ and it is as wide as the artwork rather than as wide as the screen. */
2022
+ .tamga-art-blank-floor {
2023
+ height: 1px;
2024
+ background: var(--color-edge);
2025
+ }
2026
+
2027
+ /* The tile: a card you PICK, so it is raised and it presses — a template
2028
+ that cannot be chosen has no business looking like a card. */
2029
+ .tamga-art-tile {
2030
+ display: flex;
2031
+ flex-direction: column;
2032
+ min-width: 0;
2033
+ text-align: left;
2034
+ border-radius: var(--radius-card);
2035
+ border: 1px solid var(--color-edge);
2036
+ background: var(--color-shell);
2037
+ box-shadow: 2px 2px 0 var(--color-edge);
2038
+ overflow: hidden;
2039
+ cursor: pointer;
2040
+ transition: transform var(--duration-press) var(--ease-standard),
2041
+ box-shadow var(--duration-press) var(--ease-standard),
2042
+ border-color var(--duration-press) var(--ease-standard);
2043
+ }
2044
+ .tamga-art-tile:hover {
2045
+ transform: translate(-1px, -1px);
2046
+ box-shadow: 3px 3px 0 var(--color-edge);
2047
+ border-color: var(--color-accent-line);
2048
+ }
2049
+ .tamga-art-tile:active {
2050
+ transform: translate(2px, 2px);
2051
+ box-shadow: 0 0 0 var(--color-edge);
2052
+ }
2053
+ /* The category colour is a RULE, not a field. A wall of coloured banners
2054
+ would out-shout a real event; a 3px spine under the art carries the
2055
+ same information and outranks nothing. */
2056
+ /* The five tones are spelled out rather than composed from a custom property:
2057
+ Tailwind only emits an @theme variable that something actually references,
2058
+ and a var() built inside a template literal is invisible to it — the token
2059
+ resolves to nothing at runtime and the rule silently goes grey. Naming them
2060
+ here is what keeps them alive. */
2061
+ .tamga-art-spine {
2062
+ height: 3px;
2063
+ background: var(--color-edge);
2064
+ }
2065
+ .tamga-art-spine[data-tone="1"] {
2066
+ background: var(--color-chart-1);
2067
+ }
2068
+ .tamga-art-spine[data-tone="2"] {
2069
+ background: var(--color-chart-2);
2070
+ }
2071
+ .tamga-art-spine[data-tone="3"] {
2072
+ background: var(--color-chart-3);
2073
+ }
2074
+ .tamga-art-spine[data-tone="4"] {
2075
+ background: var(--color-chart-4);
2076
+ }
2077
+ .tamga-art-spine[data-tone="5"] {
2078
+ background: var(--color-chart-5);
2079
+ }
2080
+
2081
+ .tamga-scroll-x {
2082
+ overflow-x: auto;
2083
+ }
2084
+ }
2085
+
2086
+ @keyframes tamga-rise {
2087
+ from {
2088
+ opacity: 0;
2089
+ transform: translateY(8px);
2090
+ }
2091
+ to {
2092
+ opacity: 1;
2093
+ transform: none;
2094
+ }
2095
+ }
2096
+
2097
+ @keyframes tamga-toast-in {
2098
+ from {
2099
+ opacity: 0;
2100
+ transform: translateX(16px);
2101
+ }
2102
+ to {
2103
+ opacity: 1;
2104
+ transform: none;
2105
+ }
2106
+ }
2107
+
2108
+ @keyframes tamga-bar {
2109
+ 0%,
2110
+ 100% {
2111
+ transform: scaleY(0.3);
2112
+ }
2113
+ 50% {
2114
+ transform: scaleY(1);
2115
+ }
2116
+ }
2117
+ @keyframes tamga-pulse {
2118
+ 0%,
2119
+ 100% {
2120
+ opacity: 1;
2121
+ }
2122
+ 50% {
2123
+ opacity: 0.25;
2124
+ }
2125
+ }
2126
+
2127
+ .tamga-rise {
2128
+ animation: tamga-rise var(--duration-slow) var(--ease-standard) both;
2129
+ }
2130
+
2131
+ .tamga-live {
2132
+ animation: tamga-pulse var(--duration-beacon) var(--ease-breath) infinite;
2133
+ }
2134
+
2135
+ /* Deep breath, chosen over the shallow one. The shallow version was correct in
2136
+ principle and invisible in practice — on a real page you could not tell a
2137
+ loading panel from a settled one at a glance, which is the only job it has.
2138
+ 0.78 → 0.06 at 1.1s is far enough and fast enough to register peripherally
2139
+ without ever becoming a shimmer. */
2140
+ @keyframes tamga-breathe {
2141
+ 0%,
2142
+ 100% {
2143
+ opacity: 0.78;
2144
+ }
2145
+ 50% {
2146
+ opacity: 0.06;
2147
+ }
2148
+ }
2149
+
2150
+ /* Al breathing. Slow and tiny on purpose — a mascot that moves a lot turns into
2151
+ a distraction on a screen people watch for events. Whole pixels only, or
2152
+ the pixel art lands on a half pixel and blurs on the way past. */
2153
+ /* Al floats; he never pulses — and that distinction is the whole reason a
2154
+ mascot can share a screen with the beacon at all.
2155
+ Law 3 (v2, 13-design-soul) says the beacon is the only thing that pulses.
2156
+ The mascot is exempt because it moves in a different CHANNEL: the beacon
2157
+ changes BRIGHTNESS, Al changes POSITION. The eye reads a brightness change
2158
+ as "look here" and a slow drift as "this thing is alive", so the two do not
2159
+ compete for the same attention.
2160
+ The price of that exemption: this animation must never touch opacity. If it
2161
+ ever does, Al starts competing with the one real signal on the screen. */
2162
+ @keyframes tamga-float-loop {
2163
+ 0%,
2164
+ 100% {
2165
+ transform: translateY(0);
2166
+ }
2167
+ 50% {
2168
+ transform: translateY(-3px);
2169
+ }
2170
+ }
2171
+ .tamga-float {
2172
+ animation: tamga-float-loop var(--duration-float) var(--ease-breath) infinite;
2173
+ }
2174
+
2175
+ @media (prefers-reduced-motion: reduce) {
2176
+ .tamga-rise, .tamga-live, .tamga-float, .tamga-beacon[data-live="true"] {
2177
+ animation: none;
2178
+ }
2179
+ /* a stopped skeleton must still read as a placeholder, so it keeps the bar —
2180
+ parked near the middle of the breath rather than at either end */
2181
+ .tamga-skeleton::after {
2182
+ animation: none;
2183
+ opacity: 0.42;
2184
+ }
2185
+ .tamga-toast {
2186
+ animation: none;
2187
+ }
2188
+ /* a stopped spinner must still be a visible mark, never an empty box */
2189
+ .tamga-spin-part {
2190
+ animation: none !important;
2191
+ opacity: 0.6 !important;
2192
+ transform: none !important;
2193
+ }
2194
+ /* keeps a button's width when its label swaps for a loading label */
2195
+ .tamga-swap {
2196
+ display: grid;
2197
+ place-items: center;
2198
+ }
2199
+ .tamga-swap > * {
2200
+ grid-area: 1 / 1;
2201
+ display: inline-flex;
2202
+ align-items: center;
2203
+ justify-content: center;
2204
+ gap: 8px;
2205
+ white-space: nowrap;
2206
+ }
2207
+ .tamga-swap > [data-hidden="true"] {
2208
+ visibility: hidden;
2209
+ }
2210
+
2211
+ }
2212
+
2213
+ @layer components {
2214
+ /* ------------------------------------------------------------------ *
2215
+ * SAVE BAR — stuck to the bottom of a scrolling surface.
2216
+ *
2217
+ * WHY THE `::after`. `position: sticky; bottom: 0` aligns the bar's bottom
2218
+ * edge with the surface's CONTENT box, but a surface carries its own bottom
2219
+ * padding, and content was flowing THROUGH the bar in that gap. The
2220
+ * extension covers exactly that padding; the ground is the same colour, so
2221
+ * the seam is invisible. The gap is read from `--surface-pad`, which the
2222
+ * consumer sets on the scrolling surface.
2223
+ *
2224
+ * NEGATIVE MARGIN, not a full-bleed trick: the bar spans the surface edge to
2225
+ * edge while its content stays on the same left rule as everything else.
2226
+ * ------------------------------------------------------------------ */
2227
+ .tamga-save-bar {
2228
+ position: sticky;
2229
+ bottom: 0;
2230
+ z-index: 1;
2231
+ margin-inline: calc(var(--surface-pad, 1.5rem) * -1);
2232
+ margin-top: auto;
2233
+ display: flex;
2234
+ flex-wrap: wrap;
2235
+ align-items: center;
2236
+ gap: 12px;
2237
+ border-top: 1px solid var(--color-line);
2238
+ background: var(--color-page);
2239
+ padding: 12px var(--surface-pad, 1.5rem);
2240
+ }
2241
+ .tamga-save-bar::after {
2242
+ content: "";
2243
+ position: absolute;
2244
+ inset: 100% 0 auto;
2245
+ height: var(--surface-pad, 1.5rem);
2246
+ background: var(--color-page);
2247
+ }
2248
+ }