|
178 | 178 | </div> |
179 | 179 |
|
180 | 180 | <div class="episode-info-meta"> |
181 | | - {#if subtitle != null} |
182 | | - <p class="secondary small" in:fade={fadeIn}>{subtitle}</p> |
183 | | - {:else} |
184 | | - <Skeleton width="var(--ni-220)" height="var(--ni-14)" /> |
185 | | - {/if} |
| 181 | + <div class="episode-info-meta-line"> |
| 182 | + {#if subtitle != null} |
| 183 | + <p class="secondary small" in:fade={fadeIn}>{subtitle}</p> |
| 184 | + {:else} |
| 185 | + <Skeleton width="var(--ni-220)" height="var(--ni-14)" /> |
| 186 | + {/if} |
| 187 | + </div> |
186 | 188 |
|
187 | | - {#if isCrewLoading} |
188 | | - <Skeleton width="var(--ni-140)" height="var(--ni-14)" /> |
189 | | - {:else if mainCredit} |
190 | | - <p class="small episode-main-credit" in:fade={fadeIn}> |
191 | | - <MessageWithLink |
192 | | - message={mainCredit.text} |
193 | | - href={UrlBuilder.people(mainCredit.key, mainCredit.positions)} |
194 | | - target="_self" |
195 | | - />{#if mainCredit.others?.[0]}{creditSeparator}<Link |
196 | | - href={UrlBuilder.people( |
197 | | - mainCredit.others[0].key, |
198 | | - mainCredit.positions, |
199 | | - )} |
200 | | - target="_self">{mainCredit.others[0].name}</Link |
201 | | - >{/if} |
202 | | - </p> |
203 | | - {/if} |
| 189 | + <!-- Always reserved: not every episode has a director, so the line keeps |
| 190 | + its height whether it holds a credit, a skeleton, or nothing. --> |
| 191 | + <div class="episode-info-meta-line"> |
| 192 | + {#if isCrewLoading || !entry} |
| 193 | + <Skeleton width="var(--ni-140)" height="var(--ni-14)" /> |
| 194 | + {:else if mainCredit} |
| 195 | + <p class="small episode-main-credit" in:fade={fadeIn}> |
| 196 | + <MessageWithLink |
| 197 | + message={mainCredit.text} |
| 198 | + href={UrlBuilder.people(mainCredit.key, mainCredit.positions)} |
| 199 | + target="_self" |
| 200 | + />{#if mainCredit.others?.[0]}{creditSeparator}<Link |
| 201 | + href={UrlBuilder.people( |
| 202 | + mainCredit.others[0].key, |
| 203 | + mainCredit.positions, |
| 204 | + )} |
| 205 | + target="_self">{mainCredit.others[0].name}</Link |
| 206 | + >{/if} |
| 207 | + </p> |
| 208 | + {/if} |
| 209 | + </div> |
204 | 210 | </div> |
205 | 211 |
|
206 | 212 | <div class="episode-info-actions"> |
|
215 | 221 | /> |
216 | 222 | </RenderFor> |
217 | 223 |
|
218 | | - <div class="episode-info-rate-now"> |
219 | | - <RateNow type="episode" media={entry} {show} /> |
220 | | - </div> |
| 224 | + <RateNow type="episode" media={entry} {show} /> |
221 | 225 | </div> |
222 | 226 | {:else} |
223 | 227 | <Skeleton |
|
237 | 241 | label={m.button_label_expand_media_overview({ |
238 | 242 | title: show.title, |
239 | 243 | })} |
240 | | - lineCount={6} |
| 244 | + lineCount={3} |
241 | 245 | > |
242 | 246 | {entry.overview} |
243 | 247 | </ClampedText> |
|
355 | 359 | text-align: center; |
356 | 360 | } |
357 | 361 |
|
| 362 | + // Each line keeps a fixed height so swapping skeleton <-> text, or having no |
| 363 | + // credit at all, never shifts the layout below. |
| 364 | + .episode-info-meta-line { |
| 365 | + display: flex; |
| 366 | + align-items: center; |
| 367 | + justify-content: center; |
| 368 | + min-height: var(--ni-16); |
| 369 | + } |
| 370 | +
|
358 | 371 | .episode-main-credit { |
359 | 372 | :global(.trakt-link) { |
360 | 373 | text-decoration-thickness: var(--ni-1); |
|
367 | 380 | display: flex; |
368 | 381 | flex-direction: column; |
369 | 382 | align-items: center; |
| 383 | +
|
| 384 | + // Match the skeleton / actions-bar height so the area never collapses |
| 385 | + // below it while the entry loads or when there are no actions to show. |
| 386 | + min-height: var(--ni-56); |
370 | 387 | } |
371 | 388 |
|
372 | 389 | .episode-info-actions-content { |
|
382 | 399 | } |
383 | 400 | } |
384 | 401 |
|
385 | | - .episode-info-rate-now { |
386 | | - display: flex; |
387 | | - justify-content: center; |
388 | | - } |
389 | | -
|
390 | 402 | .episode-info-overview { |
391 | 403 | align-self: stretch; |
392 | 404 |
|
393 | 405 | margin-top: var(--gap-s); |
394 | 406 |
|
395 | 407 | text-align: start; |
396 | 408 |
|
| 409 | + // Reserve exactly three lines (matching ClampedText's lineCount and 150% |
| 410 | + // line-height) so the skeleton and the loaded overview occupy the same |
| 411 | + // height and the tabs below never jump. |
| 412 | + min-height: calc(3 * var(--font-size-text) * 1.5); |
| 413 | +
|
397 | 414 | :global(.trakt-collapsable-content-button) { |
398 | 415 | width: 100%; |
399 | 416 | box-sizing: border-box; |
|
0 commit comments