@bagelink/vue 1.15.277 → 1.15.285

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.15.277",
4
+ "version": "1.15.285",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -5,7 +5,7 @@
5
5
  // stay circular), use the `innerClass` prop — mirrors `triggerClass` on Dropdown.
6
6
  defineOptions({ name: 'BglAvatar' })
7
7
  import type { IconType, ThemeType } from '@bagelink/vue'
8
- import { initials, Icon } from '@bagelink/vue'
8
+ import { initials, Icon, useDevice } from '@bagelink/vue'
9
9
  import { computed } from 'vue'
10
10
  import Image from './Image.vue'
11
11
 
@@ -16,6 +16,8 @@ const props = withDefaults(defineProps<{
16
16
  icon?: IconType
17
17
  alt?: string
18
18
  size?: number
19
+ /** Avatar size on mobile. Falls back to `size` when omitted. */
20
+ mobileSize?: number
19
21
  /** Rounded-square instead of a circle. `true` = themed radius, or pass a px radius. */
20
22
  square?: boolean | number
21
23
  /** Theme color → tinted background + matching icon/text color (e.g. an icon chip). */
@@ -29,9 +31,11 @@ const props = withDefaults(defineProps<{
29
31
  innerClass?: string
30
32
  }>(), { size: 50 })
31
33
 
34
+ const { isMobile } = useDevice()
35
+ const resolvedSize = computed(() => isMobile.value && props.mobileSize !== undefined ? props.mobileSize : props.size)
32
36
  const statusColor = computed(() => (props.status === true ? 'green' : props.status || null))
33
37
  // Dot scales with the avatar but stays within sane bounds.
34
- const dotSize = computed(() => Math.max(7, Math.min(14, Math.round(props.size * 0.28))))
38
+ const dotSize = computed(() => Math.max(7, Math.min(14, Math.round(resolvedSize.value * 0.28))))
35
39
 
36
40
  const radius = computed(() => {
37
41
  if (!props.square) { return '1000px' } // circle (default)
@@ -49,15 +53,15 @@ const themed = computed(() => props.color
49
53
  </script>
50
54
 
51
55
  <template>
52
- <div class="avatar-wrap" :style="{ width: `${size}px`, height: `${size}px` }">
56
+ <div class="avatar-wrap" :style="{ width: `${resolvedSize}px`, height: `${resolvedSize}px` }">
53
57
  <div
54
58
  class="overflow-hidden txt-center p-0 avatar flex justify-content-center align-items-center"
55
59
  :class="innerClass"
56
- :style="{ width: `${size}px`, height: `${size}px`, borderRadius: radius, ...themed }"
60
+ :style="{ width: `${resolvedSize}px`, height: `${resolvedSize}px`, borderRadius: radius, ...themed }"
57
61
  >
58
62
  <Image v-if="src" :src="src" :alt="alt || name" />
59
- <Icon v-else-if="icon" :icon="icon" :size="size / 40" />
60
- <p v-else :style="{ 'line-height': `${size}px`, 'font-size': `calc(1.5rem * ${size} / 50)` }">
63
+ <Icon v-else-if="icon" :icon="icon" :size="resolvedSize / 40" />
64
+ <p v-else :style="{ 'line-height': `${resolvedSize}px`, 'font-size': `calc(1.5rem * ${resolvedSize} / 50)` }">
61
65
  {{ (fallback || initials(name || '')).toUpperCase() }}
62
66
  </p>
63
67
  </div>
@@ -13,6 +13,9 @@ const props = withDefaults(defineProps<{
13
13
  size?: number | string
14
14
  mobileSize?: number | string
15
15
  color?: string
16
+ /** Circular chip. With `color`, fills the background from that tone
17
+ (theme name → `pair-*`, or a raw CSS color) and contrasts the glyph.
18
+ Without `color`, just the circular shape. */
16
19
  round?: boolean
17
20
  weight?: number | string
18
21
  fontAwesome?: boolean
@@ -147,21 +150,52 @@ const transitionName = computed(() => {
147
150
  const style = props.transition === true ? 'slide-up' : props.transition
148
151
  return `bgl-icon-x-${style}`
149
152
  })
153
+
154
+ const isRawCssColor = computed(() => {
155
+ const c = props.color?.trim()
156
+ if (!c) { return false }
157
+ return c.startsWith('#')
158
+ || c.startsWith('var(')
159
+ || /^(rgb|hsl|hwb|lab|lch|oklch|color)\(/i.test(c)
160
+ })
161
+
162
+ const roundPairClass = computed(() => {
163
+ if (!props.round || !props.color || isRawCssColor.value) { return undefined }
164
+ return `pair-${props.color}`
165
+ })
166
+
167
+ const roundClass = computed(() => (props.round ? 'bgl_icon-round' : undefined))
168
+
169
+ const iconStyle = computed(() => {
170
+ const style: Record<string, string> = {
171
+ fontSize: `${computedSize.value}rem`,
172
+ visibility: isCurrentFontReady.value ? 'visible' : 'hidden',
173
+ ...animateStyle.value,
174
+ }
175
+ if (iconRenderType.value === 'material') {
176
+ style['font-variation-settings'] = `'FILL' ${props.fill ? 1 : 0}, 'wght' ${props.weight || 400}`
177
+ } else {
178
+ style['font-variation-settings'] = `'wght' ${props.weight || 400}`
179
+ }
180
+ if (props.round) {
181
+ style.width = `calc(${computedSize.value}rem * 2)`
182
+ style.height = `calc(${computedSize.value}rem * 2)`
183
+ if (isRawCssColor.value && props.color) {
184
+ style.backgroundColor = props.color
185
+ }
186
+ } else if (props.color) {
187
+ style.color = props.color
188
+ }
189
+ return style
190
+ })
150
191
  </script>
151
192
 
152
193
  <template>
153
194
  <Transition :name="transitionName" mode="out-in">
154
195
  <span
155
196
  v-if="iconRenderType === 'material'" :key="iconRender" class="bgl_icon-font notranslate"
156
- :class="[animateClass, { 'round flex aspect-ratio-1 justify-content-center': round }]" :style="{
157
- 'fontSize': `${computedSize}rem`,
158
- color,
159
- 'font-variation-settings': `'FILL' ${fill ? 1 : 0}, 'wght' ${weight || 400}`,
160
- 'width': round ? `calc(${computedSize}rem * 2)` : 'auto',
161
- 'height': round ? `calc(${computedSize}rem * 2)` : 'auto',
162
- 'visibility': isCurrentFontReady ? 'visible' : 'hidden',
163
- ...animateStyle,
164
- }" translate="no"
197
+ :class="[animateClass, roundClass, roundPairClass, { 'color-white keep-white': round && isRawCssColor }]"
198
+ :style="iconStyle" translate="no"
165
199
  >
166
200
  {{ iconRender }}
167
201
  </span>
@@ -169,12 +203,15 @@ const transitionName = computed(() => {
169
203
  v-else-if="iconRenderType === 'font-awesome'" :key="iconRender" class="fa bgl_icon-font notranslate" :class="[
170
204
  `fa-${iconRender}`,
171
205
  animateClass,
206
+ roundClass,
207
+ roundPairClass,
172
208
  {
173
209
  'fa-brands': isFaBrand,
174
210
  'fa-solid': fill,
175
211
  'far': !fill && !isFaBrand,
212
+ 'color-white keep-white': round && isRawCssColor,
176
213
  },
177
- ]" :style="{ 'fontSize': `${computedSize}rem`, color, 'font-variation-settings': `'wght' ${weight || 400}`, 'visibility': isCurrentFontReady ? 'visible' : 'hidden', ...animateStyle }"
214
+ ]" :style="iconStyle"
178
215
  translate="no"
179
216
  />
180
217
  </Transition>
@@ -202,6 +239,17 @@ const transitionName = computed(() => {
202
239
  font-family: 'Material Symbols Outlined', serif !important;
203
240
  }
204
241
 
242
+ .bgl_icon-font.bgl_icon-round {
243
+ display: inline-flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ flex-shrink: 0;
247
+ aspect-ratio: 1;
248
+ border-radius: 1000px;
249
+ line-height: 1;
250
+ box-sizing: border-box;
251
+ }
252
+
205
253
  .bgl_icon-font.fa {
206
254
  font-family: 'Font Awesome 6 Free', serif !important;
207
255
  }
@@ -235,7 +235,7 @@ const headerFontSize = computed(() => {
235
235
  :icon="props.emptyIcon ?? 'inbox'"
236
236
  :title="props.emptyTitle ?? 'No results'"
237
237
  :description="props.emptyDescription"
238
- compact
238
+ class="h-60p"
239
239
  />
240
240
  </slot>
241
241
  </div>
@@ -258,7 +258,7 @@ justify-content: center;
258
258
 
259
259
  .col-check {
260
260
  width: 1rem;
261
- padding: 0 0.25rem;
261
+ padding: 0 0.5rem;
262
262
  vertical-align: middle;
263
263
  }
264
264
 
@@ -374,7 +374,7 @@ vertical-align: bottom;
374
374
 
375
375
  .row.first-row::after {
376
376
  content: '';
377
- border-bottom: 1px solid var(--bgl-border-color);
377
+ /* border-bottom: 1px solid var(--bgl-border-color); */
378
378
  position: absolute;
379
379
  left: 0;
380
380
  right: 0;
@@ -505,7 +505,7 @@ accent-color: var(--bgl-accent-color);
505
505
 
506
506
  th::after {
507
507
  content: '';
508
- border-bottom: 1px solid var(--bgl-border-color);
508
+ /* border-bottom: 1px solid var(--bgl-border-color); */
509
509
  position: absolute;
510
510
  left: 0;
511
511
  right: 0;
@@ -18,6 +18,8 @@ const props = defineProps({
18
18
  disabled: { type: Boolean, default: false },
19
19
  /** Wrap long lines instead of scrolling horizontally */
20
20
  wrap: { type: Boolean, default: false },
21
+ /** One-line input height — matches TextInput / SelectInput. Enter does not insert a newline. */
22
+ singleLine: { type: Boolean, default: false },
21
23
  theme: { type: String as PropType<HighlightTheme>, default: 'dark' },
22
24
  frame: { type: Boolean, default: undefined },
23
25
  outline: { type: Boolean, default: undefined },
@@ -51,6 +53,7 @@ const isFocused = ref(false)
51
53
  const { loaded, loadHighlight, highlightCode, setTheme } = useHighlight(props.theme)
52
54
 
53
55
  const maxHeight = computed(() => {
56
+ if (props.singleLine) return undefined
54
57
  const h = props.height
55
58
  return h.match(/^\d+$/) ? `${h}px` : h
56
59
  })
@@ -97,6 +100,14 @@ function handleInput(e: Event) {
97
100
  }
98
101
  }
99
102
 
103
+ function handleKeydown(event: KeyboardEvent) {
104
+ if (props.singleLine) {
105
+ if (event.key === 'Enter') event.preventDefault()
106
+ return
107
+ }
108
+ handleTab(event)
109
+ }
110
+
100
111
  function handleTab(event: KeyboardEvent) {
101
112
  if (event.key !== 'Tab') { return }
102
113
 
@@ -156,7 +167,7 @@ watch(() => props.modelValue, () => {
156
167
 
157
168
  <template>
158
169
  <div
159
- class="bagel-input code-editor-container ltr" :class="[shellClass, { 'mb-2': label, 'has-value': hasCodeValue }]"
170
+ class="bagel-input code-editor-container ltr" :class="[shellClass, { 'mb-2': label && !singleLine, 'has-value': hasCodeValue, 'single-line': singleLine }]"
160
171
  :style="[{ height: maxHeight }, shellStyle]"
161
172
  >
162
173
  <label v-if="label" class="label">{{ label }}</label>
@@ -172,7 +183,7 @@ watch(() => props.modelValue, () => {
172
183
  <textarea
173
184
  v-if="!readonly && !disabled" :value="code" class="code-input" spellcheck="false"
174
185
  autocomplete="off" autocorrect="off" autocapitalize="off" @input="handleInput"
175
- @keydown="handleTab"
186
+ @keydown="handleKeydown"
176
187
  @focus="isFocused = true"
177
188
  @blur="handleBlur"
178
189
  />
@@ -304,6 +315,31 @@ watch(() => props.modelValue, () => {
304
315
  overflow-wrap: anywhere;
305
316
  word-break: break-word;
306
317
  }
318
+
319
+ .code-editor-container.single-line {
320
+ height: auto;
321
+ margin-bottom: 0;
322
+ }
323
+
324
+ .code-editor-container.single-line .code-editor-grandpa {
325
+ height: var(--bgl-input-height);
326
+ padding-block: 0;
327
+ padding-inline: 0.7rem;
328
+ overflow-x: auto;
329
+ overflow-y: hidden;
330
+ background: var(--bgl-input-bg);
331
+ border-radius: var(--bgl-input-border-radius);
332
+ box-shadow: none;
333
+ }
334
+
335
+ .code-editor-container.single-line .code-display,
336
+ .code-editor-container.single-line .code-input,
337
+ .code-editor-container.single-line .code-placeholder {
338
+ padding: 0;
339
+ line-height: var(--bgl-input-height);
340
+ font-size: var(--bgl-input-font-size);
341
+ white-space: pre;
342
+ }
307
343
  </style>
308
344
 
309
345
  <style>
@@ -31,6 +31,7 @@ export interface TextInputProps extends ValidateInputBaseT, BagelInputShellProps
31
31
  nativeInputAttrs?: { [key: string]: any }
32
32
  icon?: IconType
33
33
  iconStart?: IconType
34
+ iconColor?: string
34
35
  multiline?: boolean
35
36
  autoheight?: boolean
36
37
  code?: boolean
@@ -166,6 +167,7 @@ onMounted(async () => {
166
167
  underlined,
167
168
  'textInputIconWrap': icon,
168
169
  'txtInputIconStart': iconStart,
170
+ 'multiline-field': autoheight || inputRows >= 2,
169
171
  'has-error': !!error,
170
172
  'has-value': hasValue,
171
173
  },
@@ -173,21 +175,22 @@ onMounted(async () => {
173
175
  >
174
176
  <label :for="id">
175
177
  <span v-if="label || (underlined && placeholder)" class="label-text">{{ resolveI18n(label) || resolveI18n(placeholder) }}<span v-if="required"> *</span></span>
176
-
177
- <input
178
- v-if="!autoheight && inputRows < 2" :id ref="input" v-model.trim="inputVal"
179
- :name :title :autocomplete :type :rows="1"
180
- :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))"
181
- :disabled :required v-bind="nativeInputAttrs" :pattern="normalizedPattern" @focusout="onFocusout"
182
- @focus="onFocus" @input="updateInputVal"
183
- >
184
- <textarea
185
- v-else :id ref="input" v-model="inputVal" :name :title :type :rows="inputRows"
186
- :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))" :disabled :required
187
- v-bind="nativeInputAttrs" :pattern="normalizedPattern" @input="updateInputVal" @focusout="onFocusout"
188
- />
189
- <Icon v-if="iconStart" :class="{ ' -top-025 line-height-0 ': underlined }" class="iconStart" :icon="iconStart" />
190
- <Icon v-if="icon" :class="{ ' -top-025 line-height-0 ': underlined }" :icon />
178
+ <span class="input-field">
179
+ <input
180
+ v-if="!autoheight && inputRows < 2" :id ref="input" v-model.trim="inputVal"
181
+ :name :title :autocomplete :type :rows="1"
182
+ :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))"
183
+ :disabled :required v-bind="nativeInputAttrs" :pattern="normalizedPattern" @focusout="onFocusout"
184
+ @focus="onFocus" @input="updateInputVal"
185
+ >
186
+ <textarea
187
+ v-else :id ref="input" v-model="inputVal" :name :title :type :rows="inputRows"
188
+ :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))" :disabled :required
189
+ v-bind="nativeInputAttrs" :pattern="normalizedPattern" @input="updateInputVal" @focusout="onFocusout"
190
+ />
191
+ <Icon v-if="iconStart" :class="{ ' -top-025 line-height-0 ': underlined }" class="iconStart" :icon="iconStart" :color="iconColor" />
192
+ <Icon v-if="icon" :class="{ ' -top-025 line-height-0 ': underlined }" :icon :color="iconColor" />
193
+ </span>
191
194
  </label>
192
195
  <p v-if="helptext" class="helptext">{{ helptext }}</p>
193
196
  <div v-if="error" class="error-message">
@@ -269,20 +272,15 @@ gap: 0.5rem;
269
272
  background: var(--input-disabled-bg);
270
273
  }
271
274
 
272
- /* Icon centering.
273
- The icon is absolutely positioned inside the <label>, whose only positioned ancestor
274
- is `.bagel-input`. In the DEFAULT variant the label text is in normal flow and pushes
275
- the input DOWN, but the old `top: input-height/2` was measured from the component top —
276
- so with a label the icon rode up onto the label text (the misalignment).
277
-
278
- Fix: make the <label> the containing block and anchor the icon into a band the height
279
- of the input row pinned to the BOTTOM of the label (the input is the last row), then
280
- flex-center it. Bottom-anchored + fixed band height ⇒ centered on the input regardless
281
- of a label above, and in dense/small modes. Scoped to :not(.underlined) because the
282
- underlined variant floats its label as an overlay (input isn't pushed down). */
275
+ /* Keep icon positioning relative to the native field, not the optional label. */
276
+ .input-field {
277
+ position: relative;
278
+ display: block;
279
+ width: 100%;
280
+ }
281
+
283
282
  .bagel-input:not(.underlined).textInputIconWrap > label,
284
283
  .bagel-input:not(.underlined).txtInputIconStart > label {
285
- position: relative;
286
284
  display: block;
287
285
  }
288
286
 
@@ -293,6 +291,14 @@ bottom: calc(var(--bgl-input-height) / 2);
293
291
  transform: translateY(50%);
294
292
  }
295
293
 
294
+ /* In a textarea, align icons with the first line instead of the field's bottom. */
295
+ .bagel-input.multiline-field:not(.underlined).textInputIconWrap .bgl_icon-font,
296
+ .bagel-input.multiline-field:not(.underlined).txtInputIconStart .iconStart {
297
+ top: calc(0.7rem + var(--bgl-input-font-size) / 2);
298
+ bottom: auto;
299
+ transform: translateY(-50%);
300
+ }
301
+
296
302
  /* underlined: label floats as an overlay, so the original top-center is correct */
297
303
  .underlined.textInputIconWrap .bgl_icon-font,
298
304
  .underlined.txtInputIconStart .iconStart {
@@ -300,6 +306,11 @@ top: calc(var(--bgl-input-height) / 2);
300
306
  transform: translateY(-50%);
301
307
  }
302
308
 
309
+ .underlined.multiline-field.textInputIconWrap .bgl_icon-font,
310
+ .underlined.multiline-field.txtInputIconStart .iconStart {
311
+ top: calc(1.4rem + var(--bgl-input-font-size) / 2);
312
+ }
313
+
303
314
  .textInputIconWrap .bgl_icon-font,
304
315
  .txtInputIconStart .iconStart {
305
316
  color: var(--bgl-input-color);
@@ -129,6 +129,7 @@ function buildPayload(stepKey: string): FormStepEventPayload {
129
129
  stepTitle: step?.title ?? '',
130
130
  stepIndex: position,
131
131
  totalSteps,
132
+ skipSubmit: step?.skipSubmit ?? false,
132
133
  formData: { ...formData.value },
133
134
  }
134
135
  }
@@ -178,7 +179,10 @@ async function onPrimary() {
178
179
  submitting.value = true
179
180
  const step = currentStep.value
180
181
  try {
181
- await props.onComplete?.({ ...formData.value })
182
+ // `skipSubmit` terminals (e.g. disqualification leaves) end the flow
183
+ // without creating a submission — the host's onComplete is bypassed,
184
+ // but analytics + redirect still fire.
185
+ if (!step?.skipSubmit) await props.onComplete?.({ ...formData.value })
182
186
  // Emit formComplete with standardised payload before completing.
183
187
  if (currentKey.value) emit('formComplete', buildPayload(currentKey.value))
184
188
  emit('complete', { ...formData.value })
@@ -99,7 +99,8 @@ export interface FormField {
99
99
  }
100
100
 
101
101
  export interface JSONSchemaProperty {
102
- type?: string
102
+ type?: string | string[]
103
+ items?: JSONSchemaProperty
103
104
  title?: string
104
105
  description?: string
105
106
  format?: string
@@ -298,12 +299,15 @@ export function fieldsToSchema(fields: FormField[]): JSONSchemaObject {
298
299
  case 'phone':
299
300
  case 'url':
300
301
  case 'date':
301
- case 'file':
302
302
  case 'signature':
303
303
  case 'color':
304
304
  case 'hidden':
305
305
  prop.type = 'string'
306
306
  break
307
+ case 'file':
308
+ prop.type = ['string', 'array']
309
+ prop.items = { type: 'string' }
310
+ break
307
311
  case 'number':
308
312
  case 'range':
309
313
  prop.type = 'number'
@@ -31,6 +31,12 @@ export interface FormStep {
31
31
  * (final step or an early-termination leaf). Empty => no redirect.
32
32
  */
33
33
  redirectUrl?: string | null
34
+ /**
35
+ * Terminal step that ends the flow *without* creating a submission — e.g. a
36
+ * disqualification / "not a fit" leaf. The step's own content (title + fields)
37
+ * is shown as the ending message; `redirectUrl` still applies if set.
38
+ */
39
+ skipSubmit?: boolean
34
40
  /** Canvas position (designer-only; ignored by the server). */
35
41
  x?: number
36
42
  y?: number
@@ -62,6 +68,8 @@ export interface FormStepEventPayload {
62
68
  stepIndex: number
63
69
  /** Projected total along current branch (null if indeterminate). */
64
70
  totalSteps: number | null
71
+ /** True when this terminal step ends the flow without submitting (disqualification). */
72
+ skipSubmit: boolean
65
73
  formData: Record<string, any>
66
74
  }
67
75
 
@@ -53,6 +53,17 @@
53
53
  font-family: 'Font Awesome 6 Brands', serif !important;
54
54
  }
55
55
 
56
+ .bgl_icon-font.bgl_icon-round {
57
+ display: inline-flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+ flex-shrink: 0;
61
+ aspect-ratio: 1;
62
+ border-radius: 1000px;
63
+ line-height: 1;
64
+ box-sizing: border-box;
65
+ }
66
+
56
67
  /* Reset / base element styles */
57
68
  :root {
58
69
  --bgl-transition-ease: cubic-bezier(0.1, 0.5, 0.33, 1);
@@ -37,6 +37,21 @@
37
37
  font-size: 20px;
38
38
  }
39
39
 
40
+ .txt21,
41
+ .txt-21 {
42
+ font-size: 21px;
43
+ }
44
+
45
+ .txt22,
46
+ .txt-22 {
47
+ font-size: 22px;
48
+ }
49
+
50
+ .m_txt23,
51
+ .m_txt-23 {
52
+ font-size: 23px;
53
+ }
54
+
40
55
  .txt24,
41
56
  .txt-24 {
42
57
  font-size: 24px;
@@ -77,6 +92,11 @@
77
92
  font-size: 48px;
78
93
  }
79
94
 
95
+ .txt49,
96
+ .txt-49 {
97
+ font-size: 49px;
98
+ }
99
+
80
100
  .txt50,
81
101
  .txt-50 {
82
102
  font-size: 50px;
@@ -1746,6 +1766,21 @@
1746
1766
  font-size: 20px;
1747
1767
  }
1748
1768
 
1769
+ .m_txt21,
1770
+ .m_txt-21 {
1771
+ font-size: 21px;
1772
+ }
1773
+
1774
+ .m_txt22,
1775
+ .m_txt-22 {
1776
+ font-size: 22px;
1777
+ }
1778
+
1779
+ .m_txt23,
1780
+ .m_txt-23 {
1781
+ font-size: 23px;
1782
+ }
1783
+
1749
1784
  .m_txt24,
1750
1785
  .m_txt-24 {
1751
1786
  font-size: 24px;
@@ -1786,6 +1821,11 @@
1786
1821
  font-size: 48px;
1787
1822
  }
1788
1823
 
1824
+ .m_txt49,
1825
+ .m_txt-49 {
1826
+ font-size: 49px;
1827
+ }
1828
+
1789
1829
  .m_txt50,
1790
1830
  .m_txt-50 {
1791
1831
  font-size: 50px;