@playpilot/tpi 5.6.0 → 5.7.0-beta.display-2

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.
@@ -5,23 +5,29 @@
5
5
  import DragHandle from './DragHandle.svelte'
6
6
  import { onMount, setContext, type Snippet } from 'svelte'
7
7
  import { prefersReducedMotion } from 'svelte/motion'
8
+ import { isInSplitTestVariant } from '$lib/splitTest'
9
+ import { SplitTest } from '$lib/enums/SplitTest'
8
10
 
9
11
  interface Props {
10
12
  children: Snippet
13
+ bubble?: Snippet | null
14
+ prepend?: Snippet | null
11
15
  onclose?: () => void
12
16
  onscroll?: () => void
13
17
  }
14
18
 
15
- const { children, onclose = () => null, onscroll = () => null }: Props = $props()
19
+ const { children, bubble, prepend, onclose = () => null, onscroll = () => null }: Props = $props()
20
+
21
+ const inlineBubble = isInSplitTestVariant(SplitTest.TopScrollFormat, 1)
16
22
 
17
23
  let windowWidth = $state(0)
18
24
  let dialogElement: HTMLElement | null = $state(null)
19
- let dragHandleOffset: number = $state(0)
25
+ let dialogOffset: number = $state(0)
20
26
 
21
27
  const isMobile = $derived(windowWidth < 600)
22
28
 
23
- $effect(() => { if (windowWidth) dragHandleOffset = dialogElement?.offsetTop || 0 })
24
- $effect(() => { setTimeout(() => dragHandleOffset = dialogElement?.offsetTop || 0) }) // Set after the dialog has shown to get the proper height
29
+ $effect(() => { if (windowWidth) dialogOffset = dialogElement?.offsetTop || 0 })
30
+ $effect(() => { setTimeout(() => dialogOffset = dialogElement?.offsetTop || 0) }) // Set after the dialog has shown to get the proper height
25
31
 
26
32
  setContext('scope', 'modal')
27
33
 
@@ -32,24 +38,36 @@
32
38
  return () => document.body.style.overflowY = baseOverflowStyle || ''
33
39
  })
34
40
 
35
- function scaleOrFly(node: Element): TransitionConfig {
41
+ function scaleOrFly(node: Element, options: { y: number } = { y: 0 }): TransitionConfig {
36
42
  if (prefersReducedMotion.current) return fade(node, { duration: 0 })
37
43
 
38
- if (isMobile) return fly(node, { duration: 250, y: window.innerHeight })
44
+ if (isMobile) return fly(node, { duration: 250, ...options })
39
45
  return scale(node, { duration: 150, start: 0.85 })
40
46
  }
41
47
  </script>
42
48
 
43
49
  <svelte:window onkeydown={({ key }) => { if (key === 'Escape') onclose() }} bind:innerWidth={windowWidth} />
44
50
 
45
- <div class="modal" transition:fade|global={{ duration: 150 }}>
51
+ <div class="modal" style:--dialog-offset="{dialogOffset}px" transition:fade|global={{ duration: 150 }} class:has-bubble={!!bubble && inlineBubble} class:has-prepend={!!prepend}>
52
+ {#if prepend}
53
+ <div class="prepend" transition:scaleOrFly|global={{ y: -10 }}>
54
+ {@render prepend()}
55
+ </div>
56
+ {/if}
57
+
58
+ {#if bubble}
59
+ <div class="bubble" class:inline={inlineBubble} transition:scaleOrFly|global={{ y: inlineBubble ? window.innerHeight : -10 }}>
60
+ {@render bubble()}
61
+ </div>
62
+ {/if}
63
+
46
64
  {#if isMobile}
47
- <div class="drag-handle" style:top="{dragHandleOffset}px" transition:scaleOrFly|global>
65
+ <div class="drag-handle" transition:scaleOrFly|global>
48
66
  <DragHandle target={dialogElement!} onpassed={() => onclose()} />
49
67
  </div>
50
68
  {/if}
51
69
 
52
- <div class="dialog" {onscroll} bind:this={dialogElement} role="dialog" aria-labelledby="title" transition:scaleOrFly|global data-view-transition-new>
70
+ <div class="dialog" {onscroll} bind:this={dialogElement} role="dialog" aria-labelledby="title" transition:scaleOrFly|global={{ y: window.innerHeight }} data-view-transition-new>
53
71
  <div class="close">
54
72
  <RoundButton onclick={() => onclose()}>
55
73
  <IconClose />
@@ -65,30 +83,38 @@
65
83
  </div>
66
84
 
67
85
  <style lang="scss">
86
+ $max-width: 600px;
87
+
68
88
  .modal {
89
+ --playpilot-detail-background-border-radius: var(--playpilot-detail-border-radius, #{margin(1)} #{margin(1)} 0 0);
69
90
  z-index: 2147483647; // As high as she goes
70
91
  box-sizing: border-box;
71
92
  position: fixed;
72
93
  display: flex;
73
- justify-content: center;
74
- align-items: flex-start;
94
+ flex-direction: column;
95
+ justify-content: flex-start;
96
+ align-items: center;
97
+ overflow: auto;
75
98
  top: 0;
76
99
  left: 0;
77
100
  width: 100%;
78
101
  height: 100%;
79
102
  background: var(--playpilot-detail-backdrop, rgba(0, 0, 0, 0.65));
80
103
 
81
- @media (min-width: 600px) {
82
- padding: margin(2);
104
+ @media (min-width: $max-width) {
105
+ padding: margin(2) 0;
83
106
  }
84
- }
85
107
 
108
+ &.has-bubble {
109
+ --playpilot-detail-background-border-radius: 0;
110
+ }
111
+ }
86
112
 
87
113
  .dialog {
88
114
  z-index: 1;
89
115
  position: relative;
90
116
  width: 100%;
91
- max-width: 600px;
117
+ max-width: $max-width;
92
118
  max-height: 80vh;
93
119
  overflow: auto;
94
120
  margin-top: auto;
@@ -96,10 +122,21 @@
96
122
  background: var(--playpilot-detail-background, var(--playpilot-light));
97
123
  transition: transform 200ms;
98
124
 
99
- @media (min-width: 600px) {
125
+ @media (min-width: $max-width) {
100
126
  margin-top: 0;
101
127
  border-radius: var(--playpilot-detail-border-radius, margin(1));
102
- max-height: 100%;
128
+ overflow: visible;
129
+ max-height: unset;
130
+ }
131
+
132
+ .has-prepend & {
133
+ margin-top: 0;
134
+ }
135
+
136
+ .has-bubble & {
137
+ border-top-left-radius: 0;
138
+ border-top-right-radius: 0;
139
+ margin-top: 0;
103
140
  }
104
141
  }
105
142
 
@@ -115,6 +152,7 @@
115
152
  .drag-handle {
116
153
  z-index: 5;
117
154
  position: absolute;
155
+ top: var(--dialog-offset);
118
156
  left: 50%;
119
157
  transform: translateY(margin(-0.5)) translateX(-50%);
120
158
  width: margin(7);
@@ -127,12 +165,59 @@
127
165
  --playpilot-button-padding: var(--playpilot-modal-close-button-padding, 0.5rem);
128
166
  --playpilot-button-text-color: var(--playpilot-modal-close-button-text-color, var(--playpilot-text-color-alt));
129
167
  z-index: 5;
130
- position: absolute;
131
- top: margin(1);
168
+ position: fixed;
169
+ top: calc(var(--dialog-offset) + margin(1));
132
170
  right: margin(1);
133
171
 
172
+ @media (min-width: $max-width) {
173
+ position: absolute;
174
+ top: margin(1);
175
+ }
176
+
134
177
  &:hover {
135
178
  filter: brightness(1.1);
136
179
  }
137
180
  }
181
+
182
+ .prepend {
183
+ z-index: 1;
184
+ position: relative;
185
+ width: calc(100% - margin(1));
186
+ max-width: $max-width;
187
+ padding-top: margin(3);
188
+ margin: auto auto margin(0.5);
189
+
190
+ @media (min-width: $max-width) {
191
+ padding-top: 0;
192
+ margin-top: 0;
193
+ }
194
+ }
195
+
196
+ .bubble {
197
+ z-index: 1;
198
+ position: relative;
199
+ width: calc(100% - margin(1));
200
+ max-width: $max-width;
201
+ margin: margin(0.5);
202
+
203
+ @media (min-width: $max-width) {
204
+ width: 100%;
205
+ margin: 0 0 margin(0.5);
206
+ }
207
+
208
+ &.inline {
209
+ width: 100%;
210
+ margin: auto 0 0;
211
+
212
+ @media (min-width: $max-width) {
213
+ margin-top: 0;
214
+ }
215
+ }
216
+
217
+ .prepend + & {
218
+ &:not(.inline) {
219
+ margin-top: 0;
220
+ }
221
+ }
222
+ }
138
223
  </style>
@@ -0,0 +1,185 @@
1
+ <script lang="ts">
2
+ import { removeImageUrlPrefix } from '$lib/image'
3
+ import { t } from '$lib/localization'
4
+ import type { PlaylinkData } from '$lib/types/playlink'
5
+
6
+ interface Props {
7
+ playlink: PlaylinkData
8
+ hideCategory?: boolean
9
+ onclick?: () => void
10
+ }
11
+
12
+ const { playlink, hideCategory = false, onclick = () => null }: Props = $props()
13
+
14
+ const { name, url, logo_url, highlighted, cta_text, action_text, extra_info: { category } } = $derived(playlink)
15
+
16
+ const categoryStrings = {
17
+ SVOD: t('Stream'),
18
+ BUY: t('Buy'),
19
+ RENT: t('Rent'),
20
+ TVOD: t('Rent Or Buy'),
21
+ }
22
+ </script>
23
+
24
+ <a href={url} target="_blank" class="playlink" class:highlighted={highlighted && cta_text} class:no-category={hideCategory} {onclick} data-playlink={name} rel="sponsored">
25
+ <div class="playlink-content">
26
+ <img src={removeImageUrlPrefix(logo_url)} alt="" height="36" width="36" />
27
+
28
+ <span class="name">{name}</span>
29
+
30
+ {#if !hideCategory}
31
+ <span class="category" data-testid="category">{categoryStrings[category] || t('Stream')}</span>
32
+ {/if}
33
+
34
+ {#if cta_text}
35
+ <span class="cta">{cta_text}</span>
36
+ {/if}
37
+
38
+ <div class="action">
39
+ {action_text || t('Watch')}
40
+ </div>
41
+ </div>
42
+ </a>
43
+
44
+ <style lang="scss">
45
+ $image-size: margin(2.25);
46
+
47
+ @keyframes sheen {
48
+ 0%, 20%, 60%, 80.01%, 100% {
49
+ background-position: -100% 0;
50
+ }
51
+
52
+ 80% {
53
+ background-position: 100% 0;
54
+ }
55
+ }
56
+
57
+ img {
58
+ height: $image-size;
59
+ width: $image-size;
60
+ border-radius: margin(0.5);
61
+ background: rgba(0, 0, 0, 0.25);
62
+ }
63
+
64
+ .playlink {
65
+ position: relative;
66
+ background: var(--playpilot-playlink-background, var(--playpilot-lighter));
67
+ box-shadow: var(--playpilot-playlink-shadow, var(--playpilot-shadow));
68
+ border-radius: var(--playpilot-playlink-border-radius, margin(0.5));
69
+ color: var(--playpilot-playlink-text-color, var(--playpilot-text-color)) !important;
70
+ font-weight: var(--playpilot-playlink-font-weight, inherit);
71
+ font-style: var(--playpilot-playlink-font-style, normal) !important;
72
+ text-decoration: none !important;
73
+ white-space: nowrap;
74
+ font-size: var(--playpilot-playlinks-font-size, margin(0.75));
75
+ line-height: 1;
76
+
77
+ &:hover,
78
+ &:active {
79
+ filter: var(--playpilot-playlink-hover-filter, brightness(1.1));
80
+ background: var(--playpilot-playlink-hover-background, var(--playpilot-playlink-background, var(--playpilot-lighter))) !important;
81
+ text-decoration: none !important;
82
+ }
83
+
84
+ &.highlighted {
85
+ grid-column: span 2;
86
+
87
+ &::before {
88
+ content: "";
89
+ z-index: 1;
90
+ display: block;
91
+ position: absolute;
92
+ top: 0;
93
+ right: 0;
94
+ bottom: 0;
95
+ left: 0;
96
+ border-radius: inherit;
97
+ background: linear-gradient(to left, currentColor 20%, transparent 50%, currentColor 80%);
98
+ background-size: 200% 100%;
99
+ background-position: -100% 0;
100
+ opacity: 0.35;
101
+ pointer-events: none;
102
+
103
+ @media (prefers-reduced-motion: no-preference) {
104
+ animation: sheen 4000ms ease-in-out infinite;
105
+ }
106
+ }
107
+
108
+ &::after {
109
+ content: "";
110
+ z-index: 2;
111
+ display: block;
112
+ position: absolute;
113
+ top: 2px;
114
+ right: 2px;
115
+ bottom: 2px;
116
+ left: 2px;
117
+ border-radius: inherit;
118
+ background: var(--playpilot-playlink-background, var(--playpilot-lighter));
119
+ }
120
+ }
121
+
122
+ img {
123
+ grid-area: image;
124
+ margin: 0;
125
+ }
126
+ }
127
+
128
+ .playlink-content {
129
+ position: relative;
130
+ z-index: 5;
131
+ display: grid;
132
+ grid-template-areas: "image name action" "image category action";
133
+ grid-template-columns: $image-size auto margin(6);
134
+ align-items: center;
135
+ gap: 0 margin(0.75);
136
+ padding: margin(0.75);
137
+
138
+ .highlighted & {
139
+ grid-template-areas: "image name action" "image category action" "image cta action";
140
+ gap: margin(0.25) margin(0.75);
141
+ }
142
+
143
+ .no-category & {
144
+ grid-template-areas: "image name action" "image cta action";
145
+ }
146
+ }
147
+
148
+ .name {
149
+ grid-area: name;
150
+ width: 100%;
151
+ overflow: hidden;
152
+ font-weight: var(--playpilot-playlink-font-weight, 500);
153
+ font-family: var(--playpilot-playlink-font-family, inherit);
154
+ text-overflow: ellipsis;
155
+ white-space: nowrap;
156
+ }
157
+
158
+ .category {
159
+ grid-area: category;
160
+ width: 100%;
161
+ font-size: var(--playpilot-playlinks-category-font-size, margin(0.625));
162
+ color: var(--playpilot-playlink-category-text-color, var(--playpilot-text-color-alt));
163
+ font-weight: var(--playpilot-playlink-category-font-weight, inherit);
164
+ font-family: var(--playpilot-playlink-category-font-family, inherit);
165
+ }
166
+
167
+ .cta {
168
+ grid-area: cta;
169
+ width: 100%;
170
+ font-size: var(--playpilot-playlinks-cta-font-size, var(--playpilot-playlinks-category-font-size, margin(0.625)));
171
+ color: var(--playpilot-playlink-cta-text-color, var(--playpilot-playlink-category-text-color, var(--playpilot-text-color)));
172
+ font-weight: var(--playpilot-playlink-cta-font-weight, var(--playpilot-playlink-category-font-weight, inherit));
173
+ font-family: var(--playpilot-playlink-cta-font-family, var(--playpilot-playlink-category-font-family, inherit));
174
+ }
175
+
176
+ .action {
177
+ grid-area: action;
178
+ margin-left: auto;
179
+ padding: margin(0.5);
180
+ border: var(--playpilot-playlinks-action-border, 1px solid currentColor);
181
+ border-radius: var(--playpilot-playlinks-action-border-radius, margin(2));
182
+ font-weight: var(--playpilot-playlinks-action-font-weight, 500);
183
+ color: var(--playpilot-playlinks-action-text-color, var(--playpilot-text-color));
184
+ }
185
+ </style>
@@ -6,8 +6,12 @@
6
6
  import type { PlaylinkData } from '$lib/types/playlink'
7
7
  import type { TitleData } from '$lib/types/title'
8
8
  import { heading } from '$lib/actions/heading'
9
+ import { isInSplitTestVariant, trackSplitTestAction } from '$lib/splitTest'
10
+ import { SplitTest } from '$lib/enums/SplitTest'
9
11
  import { getContext } from 'svelte'
10
- import { removeImageUrlPrefix } from '$lib/image'
12
+ import Playlink from './Playlink.svelte'
13
+ import Display from './Ads/Display.svelte'
14
+ import { campaignToPlaylink, getFirstAdOfType } from '$lib/ads'
11
15
 
12
16
  interface Props {
13
17
  playlinks: PlaylinkData[]
@@ -17,22 +21,21 @@
17
21
  const { playlinks, title }: Props = $props()
18
22
 
19
23
  const isModal = getContext('scope') === 'modal'
24
+ const displayAd = getFirstAdOfType('card')
25
+ const showDisplayAd = displayAd && isModal && isInSplitTestVariant(SplitTest.DisplayAdPosition, 0)
26
+ const showPlaylinkAd = displayAd && isModal && isInSplitTestVariant(SplitTest.DisplayAdPosition, 1)
20
27
 
21
28
  let outerWidth = $state(0)
22
29
 
23
- const list = $derived(outerWidth < 500)
30
+ // Grid turns into a list when the playlinks container is small enough
31
+ // It is also a list by default if a display ad is present, as that would
32
+ // otherwise break the layout in ways that don't make sense to fix.
33
+ const list = $derived(outerWidth < 500 || showDisplayAd || showPlaylinkAd)
24
34
 
25
- // Remove any playlinks without logos, as these are likely sub providers.
35
+ // Remove any playlinks without logos, these are likely sub providers.
26
36
  const filteredPlaylinks = $derived(playlinks.filter(playlink => !!playlink.logo_url))
27
37
  const mergedPlaylink = $derived(mergePlaylinks(filteredPlaylinks))
28
38
 
29
- const categoryStrings = {
30
- SVOD: t('Stream'),
31
- BUY: t('Buy'),
32
- RENT: t('Rent'),
33
- TVOD: t('Rent Or Buy'),
34
- }
35
-
36
39
  function onclick(playlink: string): void {
37
40
  track(isModal ? TrackingEvent.TitleModalPlaylinkClick : TrackingEvent.TitlePopoverPlaylinkClick, title, { playlink })
38
41
  }
@@ -48,42 +51,25 @@
48
51
  {/if}
49
52
 
50
53
  <div class="playlinks" class:list bind:clientWidth={outerWidth}>
51
- {#each mergedPlaylink as { name, url, logo_url, highlighted, cta_text, extra_info: { category } }}
52
- <a href={url} target="_blank" class="playlink" class:highlighted={highlighted && cta_text} onclick={() => onclick(name)} data-playlink={name} rel="sponsored">
53
- <div class="playlink-content">
54
- <img src={removeImageUrlPrefix(logo_url)} alt="" height="36" width="36" />
55
-
56
- <span class="name">{name}</span>
57
- <span class="category">{categoryStrings[category] || t('Stream')}</span>
58
-
59
- {#if cta_text}
60
- <span class="cta">{cta_text}</span>
61
- {/if}
62
-
63
- <div class="action">
64
- {t('Watch')}
65
- </div>
66
- </div>
67
- </a>
54
+ {#each mergedPlaylink as playlink, index}
55
+ <Playlink {playlink} onclick={() => onclick(playlink.name)} />
56
+
57
+ <!-- Insert display ad after the first playlink -->
58
+ {#if showDisplayAd && (index === 0)}
59
+ <Display campaign={displayAd} />
60
+ {:else if showPlaylinkAd && (index === 0)}
61
+ <Playlink playlink={campaignToPlaylink(displayAd)} onclick={() => trackSplitTestAction(SplitTest.DisplayAdPosition, 'click_playlink_ad')} hideCategory />
62
+ {/if}
68
63
  {/each}
69
64
 
70
- {#if !playlinks.length}
71
- <div class="playlink empty" data-testid="playlinks-empty">
65
+ {#if !mergedPlaylink.length}
66
+ <div class="empty" data-testid="playlinks-empty">
72
67
  {t('Title Unavailable')}
73
68
  </div>
74
69
  {/if}
75
70
  </div>
76
71
 
77
72
  <style lang="scss">
78
- $image-size: margin(2.25);
79
-
80
- img {
81
- height: $image-size;
82
- width: $image-size;
83
- border-radius: margin(0.5);
84
- background: rgba(0, 0, 0, 0.25);
85
- }
86
-
87
73
  .heading {
88
74
  margin: 0;
89
75
  color: var(--playpilot-playlinks-title-color, var(--playpilot-text-color-alt));
@@ -104,130 +90,11 @@
104
90
 
105
91
  &.list {
106
92
  grid-template-columns: 1fr;
107
- }
108
- }
109
-
110
-
111
- @keyframes sheen {
112
- 0%, 20%, 60%, 80.01%, 100% {
113
- background-position: -100% 0;
114
- }
115
-
116
- 80% {
117
- background-position: 100% 0;
118
- }
119
- }
120
-
121
- .playlink {
122
- position: relative;
123
- background: var(--playpilot-playlink-background, var(--playpilot-lighter));
124
- box-shadow: var(--playpilot-playlink-shadow, var(--playpilot-shadow));
125
- border-radius: var(--playpilot-playlink-border-radius, margin(0.5));
126
- color: var(--playpilot-playlink-text-color, var(--playpilot-text-color)) !important;
127
- font-weight: var(--playpilot-playlink-font-weight, inherit);
128
- font-style: var(--playpilot-playlink-font-style, normal) !important;
129
- text-decoration: none !important;
130
- white-space: nowrap;
131
- font-size: var(--playpilot-playlinks-font-size, margin(0.75));
132
- line-height: 1;
133
-
134
- &:hover,
135
- &:active {
136
- filter: var(--playpilot-playlink-hover-filter, brightness(1.1));
137
- background: var(--playpilot-playlink-hover-background, var(--playpilot-playlink-background, var(--playpilot-lighter))) !important;
138
- text-decoration: none !important;
139
- }
140
-
141
- &.highlighted {
142
- grid-column: span 2;
143
-
144
- .list & {
145
- grid-column: 1;
146
- }
147
-
148
- &::before {
149
- content: "";
150
- z-index: 1;
151
- display: block;
152
- position: absolute;
153
- top: 0;
154
- right: 0;
155
- bottom: 0;
156
- left: 0;
157
- border-radius: inherit;
158
- background: linear-gradient(to left, currentColor 20%, transparent 50%, currentColor 80%);
159
- background-size: 200% 100%;
160
- background-position: -100% 0;
161
- opacity: 0.35;
162
- pointer-events: none;
163
-
164
- @media (prefers-reduced-motion: no-preference) {
165
- animation: sheen 4000ms ease-in-out infinite;
166
- }
167
- }
168
93
 
169
- &::after {
170
- content: "";
171
- z-index: 2;
172
- display: block;
173
- position: absolute;
174
- top: 2px;
175
- right: 2px;
176
- bottom: 2px;
177
- left: 2px;
178
- border-radius: inherit;
179
- background: var(--playpilot-playlink-background, var(--playpilot-lighter));
94
+ :global(.playlink.highlighted) {
95
+ grid-column: span 1;
180
96
  }
181
97
  }
182
-
183
- img {
184
- grid-area: image;
185
- margin: 0;
186
- }
187
- }
188
-
189
- .playlink-content {
190
- position: relative;
191
- z-index: 5;
192
- display: grid;
193
- grid-template-areas: "image name action" "image category action";
194
- grid-template-columns: $image-size auto margin(4);
195
- align-items: center;
196
- gap: 0 margin(0.75);
197
- padding: margin(0.75);
198
-
199
- .highlighted & {
200
- grid-template-areas: "image name action" "image category action" "image cta action";
201
- gap: margin(0.25) margin(0.75);
202
- }
203
- }
204
-
205
- .name {
206
- grid-area: name;
207
- width: 100%;
208
- overflow: hidden;
209
- font-weight: var(--playpilot-playlink-font-weight, 500);
210
- font-family: var(--playpilot-playlink-font-family, inherit);
211
- text-overflow: ellipsis;
212
- white-space: nowrap;
213
- }
214
-
215
- .category {
216
- grid-area: category;
217
- width: 100%;
218
- font-size: var(--playpilot-playlinks-category-font-size, margin(0.625));
219
- color: var(--playpilot-playlink-category-text-color, var(--playpilot-text-color-alt));
220
- font-weight: var(--playpilot-playlink-category-font-weight, inherit);
221
- font-family: var(--playpilot-playlink-category-font-family, inherit);
222
- }
223
-
224
- .cta {
225
- grid-area: cta;
226
- width: 100%;
227
- font-size: var(--playpilot-playlinks-cta-font-size, var(--playpilot-playlinks-category-font-size, margin(0.625)));
228
- color: var(--playpilot-playlink-cta-text-color, var(--playpilot-playlink-category-text-color, var(--playpilot-text-color)));
229
- font-weight: var(--playpilot-playlink-cta-font-weight, var(--playpilot-playlink-category-font-weight, inherit));
230
- font-family: var(--playpilot-playlink-cta-font-family, var(--playpilot-playlink-category-font-family, inherit));
231
98
  }
232
99
 
233
100
  .disclaimer {
@@ -249,17 +116,16 @@
249
116
  }
250
117
 
251
118
  .empty {
119
+ grid-column: span 2;
120
+ padding: margin(0.75);
121
+ background: var(--playpilot-playlink-background, var(--playpilot-lighter));
122
+ box-shadow: var(--playpilot-playlink-shadow, var(--playpilot-shadow));
123
+ border-radius: var(--playpilot-playlink-border-radius, margin(0.5));
252
124
  white-space: initial;
253
125
  line-height: 1.35;
254
- }
255
126
 
256
- .action {
257
- grid-area: action;
258
- margin-left: auto;
259
- padding: margin(0.5);
260
- font-weight: var(--playpilot-playlinks-action-font-weight, 500);
261
- color: var(--playpilot-playlinks-action-text-color, var(--playpilot-text-color));
262
- border: var(--playpilot-playlinks-action-border, 1px solid currentColor);
263
- border-radius: var(--playpilot-playlinks-action-border-radius, margin(2));
127
+ .list & {
128
+ grid-column: 1;
129
+ }
264
130
  }
265
131
  </style>