/* ==========================================================================
   youtube.css — the YouTube downloader screen
   (conversion/templates/youtube/youtube.html).

   The first stylesheet the conversion app has ever had: the screen it replaces
   carried no CSS of its own at all. It hand-wrote the dashboard shell — cover,
   sidebar include, its own col-lg-9 — around an <h5 class="text-uppercase">, an
   <hr>, and one `<input class="foodbakery-dev-req-field">` inside a `.row` whose
   only child was a full-width column. Those two class names come from the theme
   this project was built on and are styled by whatever custom.css happens to say
   that week.

   Same contract as shorturl.css, simplenote.css and fbms.css beside it:

     * every selector is prefixed .yt-scope, matching the wrapper <div> the
       template opens with. accounts/base.html renders {% block extra_css %}
       BEFORE the theme, MDB, the Bootstrap 4.3.1 CDN build and custom.css, so
       this file can only ever win on specificity, never on order;
     * no :root block — main.models.ColorPreset injects the palette after every
       stylesheet, and a :root here would override the admin's chosen colours;
     * --color-primary and its siblings are never redeclared: defining one in
       terms of itself is a var() cycle that blanks it for the whole subtree.
       They are read inline with a literal fallback instead;
     * only Montserrat and Open Sans 400, 700 and 800 are loaded, so there is no
       font-weight 500 or 600 below;
     * layout is CSS grid, never Bootstrap's row/col. Two Bootstrap majors are
       live in this shell — MDB5 3.3.0 and the 4.3.1 CDN build loading after
       it — so `g-*` gutters are cancelled by 4.3.1's own `.row`, and
       `rounded-4`, `bg-soft-primary` and `border-dashed` are defined by nothing.

   Tokens are shorturl.css's verbatim, which are simplenote.css's verbatim. A
   visitor crossing from the shortener to the media tools is inside one account
   dashboard and should not cross a seam.
   ========================================================================== */
.yt-scope {
  --yt-surface: #ffffff;
  --yt-surface-muted: #fbfbfc;
  --yt-surface-sunken: #f4f5f8;
  --yt-border: #eceef4;
  --yt-border-strong: #e4e6ee;

  --yt-text: var(--color-default, #2f313a);
  --yt-text-soft: #6c707a;
  --yt-text-muted: #9097a1;

  --yt-brand: var(--color-primary, #c33332);
  --yt-brand-hover: var(--color-primary-hover, #a82b2a);
  --yt-brand-bg: var(--color-primary-light, #fbf2f2);
  --yt-good: #1e8e3e;
  --yt-good-bg: #eaf5ee;
  --yt-good-border: #bfe0c9;
  --yt-warn: #9a6a00;
  --yt-warn-bg: #fdf6e6;
  --yt-warn-border: #ecd9a6;
  --yt-danger: #c33332;
  --yt-danger-bg: #fdf1f1;
  --yt-danger-border: #f0cccc;

  --yt-font-display: "Montserrat", "Noto Sans Bengali", -apple-system,
    BlinkMacSystemFont, "Segoe UI", sans-serif;
  --yt-font-body: "Open Sans", "Noto Sans Bengali", -apple-system,
    BlinkMacSystemFont, "Segoe UI", sans-serif;
  --yt-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --yt-radius-sm: 4px;
  --yt-radius-md: 5px;

  padding: 4px 0 32px;
  font-family: var(--yt-font-body);
  color: var(--yt-text);
}

/* mdb.min.css uppercases and drop-shadows every button on the page and
   Bootstrap 4.3.1, loading after it, resets neither. These screens use .yt-btn
   rather than .btn for that reason; the reset stays as insurance for anything
   the shell renders inside the wrapper. */
.yt-scope .btn {
  text-transform: none;
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   Page head. 21px Montserrat in the body ink — the same metric as
   .dash-head__name on the account dashboard and .su-title on the shortener. The
   old heading was an <h5 class="text-uppercase">, which the shell's own
   !important h1..h6 rules were already fighting.

   Everything here is a <p>, never an <h1>: cs-foodbakery-plugin.css declares
   font, letter-spacing, text-transform and colour on h1-h6 with !important.
   That dodges the headings, but accounts/base.html then writes
   `.main-section p { ... color: #515151 !important }` into an inline <style>
   AFTER extra_css, so every paragraph below has to restate its colour with
   !important to keep it. Specificity carries the rest — `.yt-scope .yt-lede` is
   0,2,0 against that rule's 0,1,1.
   -------------------------------------------------------------------------- */
.yt-scope .yt-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.yt-scope .yt-eyebrow {
  margin: 0 0 5px;
  font-family: var(--yt-font-display);
  font-size: 11px;
  font-weight: 700;
  line-height: 16px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--yt-text-muted) !important;
}

.yt-scope .yt-title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  font-family: var(--yt-font-display);
  font-size: 21px;
  font-weight: 700;
  line-height: 28px;
  letter-spacing: 0;
  text-transform: none;
  color: var(--yt-text) !important;
}

.yt-scope .yt-lede {
  max-width: 62ch;
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 20px;
  color: var(--yt-text-soft) !important;
}

/* --------------------------------------------------------------------------
   Cards. One per step, numbered, so the screen reads as a sequence rather than
   as a form with an unexplained empty area under it.
   -------------------------------------------------------------------------- */
.yt-scope .yt-card {
  border: 1px solid var(--yt-border);
  border-radius: var(--yt-radius-md);
  background: var(--yt-surface);
  overflow: hidden;
}

.yt-scope .yt-card + .yt-card { margin-top: 16px; }

.yt-scope .yt-card__head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 13px 16px;
  border-bottom: 1px solid var(--yt-border);
  background: var(--yt-surface-muted);
}

.yt-scope .yt-card__title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-family: var(--yt-font-display);
  font-size: 13px;
  font-weight: 700;
  line-height: 20px;
  letter-spacing: 0.2px;
  text-transform: none;
  color: var(--yt-text) !important;
}

.yt-scope .yt-card__title i { font-size: 14px; color: var(--yt-brand); }
.yt-scope .yt-card__body { padding: 16px; }

/* The step number: a 22px disc in the brand, so "1" and "2" read as order and
   not as decoration. */
.yt-scope .yt-step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: 50%;
  background: var(--yt-brand);
  font-family: var(--yt-font-display);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: #ffffff;
}

.yt-scope .yt-step.is-waiting {
  background: var(--yt-surface-sunken);
  border: 1px solid var(--yt-border-strong);
  color: var(--yt-text-muted);
}

/* --------------------------------------------------------------------------
   Buttons. 34px tall, 12px Montserrat 700, no uppercase and no shadow — the
   .su-btn metrics. The old screen used `.btn-submit`, which the theme styles as
   a full-width block in whatever colour custom.css last set.
   -------------------------------------------------------------------------- */
.yt-scope .yt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 34px;
  padding: 0 14px;
  border: 1px solid var(--yt-brand);
  border-radius: var(--yt-radius-md);
  background: var(--yt-brand);
  font-family: var(--yt-font-display);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: 0.2px;
  text-transform: none;
  text-decoration: none;
  white-space: nowrap;
  color: #ffffff;
  box-shadow: none;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease,
    color 0.15s ease;
}

.yt-scope .yt-btn:hover,
.yt-scope .yt-btn:focus {
  background: var(--yt-brand-hover);
  border-color: var(--yt-brand-hover);
  color: #ffffff;
  text-decoration: none;
}

.yt-scope .yt-btn i { font-size: 13px; }

.yt-scope .yt-btn.is-ghost {
  background: var(--yt-surface);
  border-color: var(--yt-border-strong);
  color: var(--yt-text-soft);
}

.yt-scope .yt-btn.is-ghost:hover,
.yt-scope .yt-btn.is-ghost:focus {
  background: var(--yt-surface-sunken);
  border-color: var(--yt-text-muted);
  color: var(--yt-text);
}

.yt-scope .yt-btn.is-lg { height: 40px; padding: 0 20px; font-size: 13px; }

.yt-scope .yt-btn[disabled],
.yt-scope .yt-btn.is-busy {
  opacity: 0.55;
  pointer-events: none;
}

/* `display: inline-flex` above beats the browser's `[hidden] { display: none }`,
   so `button.hidden = true` would otherwise do nothing. */
.yt-scope .yt-btn[hidden] { display: none; }

/* --------------------------------------------------------------------------
   Fields. 38px controls, 12px Montserrat labels, 13px body text inside — the
   .su-label / .su-input metrics. The screen's single input used to carry
   `class="foodbakery-dev-req-field"`, whose height, border and focus ring are
   whatever custom.css says that week, and its label read "Vedio URL*".
   -------------------------------------------------------------------------- */
.yt-scope .yt-field + .yt-field { margin-top: 14px; }

.yt-scope .yt-label {
  display: block;
  margin: 0 0 5px;
  font-family: var(--yt-font-display);
  font-size: 12px;
  font-weight: 700;
  line-height: 18px;
  letter-spacing: 0.2px;
  color: var(--yt-text);
}

.yt-scope .yt-input {
  width: 100%;
  height: 38px;
  padding: 0 11px;
  border: 1px solid var(--yt-border-strong);
  border-radius: var(--yt-radius-md);
  background: var(--yt-surface);
  font-family: var(--yt-font-body);
  font-size: 13px;
  font-weight: 400;
  line-height: 20px;
  color: var(--yt-text);
}

.yt-scope .yt-input::placeholder { color: var(--yt-text-muted); }

.yt-scope .yt-input:focus {
  outline: none;
  border-color: var(--yt-brand);
  box-shadow: 0 0 0 3px var(--yt-brand-bg);
}

.yt-scope .yt-input.is-invalid { border-color: var(--yt-danger); }
/* The paste row: the link box and the Check button on one line, stacking under
   520px. `minmax(0, 1fr)` and not `1fr` — a grid child's automatic minimum size
   is its content, and a 500-character URL in a 13px font is wide enough to push
   the button out of the card. */
.yt-scope .yt-paste {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 8px;
  align-items: start;
}

.yt-scope .yt-paste .yt-btn { height: 38px; }

.yt-scope .yt-help {
  display: block;
  margin: 5px 0 0;
  font-size: 12px;
  line-height: 17px;
  color: var(--yt-text-soft) !important;
}

.yt-scope .yt-help code {
  padding: 1px 4px;
  border-radius: 3px;
  background: var(--yt-surface-sunken);
  font-family: var(--yt-font-mono);
  font-size: 11px;
  color: var(--yt-text);
}

.yt-scope .yt-errors { margin: 6px 0 0; padding: 0; list-style: none; }

.yt-scope .yt-errors li {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 4px;
  font-size: 12px;
  line-height: 17px;
  color: var(--yt-danger);
}

.yt-scope .yt-errors li i { margin-top: 1px; font-size: 12px; flex: none; }
/* --------------------------------------------------------------------------
   The failure line. The old view answered every exception with
   `messages.error(request, f"An error occurred: {str(e)}")`, which put the
   raw exception — regex internals, absolute paths, sometimes a signed URL —
   into the shell's alert strip above the cover photo. conversion.youtube logs
   the exception and returns one sentence, and the view now passes that sentence
   into the page rather than through the message framework, so it lands here,
   inside the card stack, where the person is looking.
   -------------------------------------------------------------------------- */
.yt-scope .yt-alert {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0 0 16px;
  padding: 11px 14px;
  border: 1px solid var(--yt-danger-border);
  border-radius: var(--yt-radius-sm);
  background: var(--yt-danger-bg);
  font-size: 12px;
  line-height: 19px;
  color: var(--yt-danger) !important;
}

.yt-scope .yt-alert i { flex: none; font-size: 14px; line-height: 19px; }
.yt-scope .yt-alert p { margin: 0; color: inherit !important; }

.yt-scope .yt-alert.is-warn {
  border-color: var(--yt-warn-border);
  background: var(--yt-warn-bg);
  color: var(--yt-warn) !important;
}

/* The polite announcer. Off-screen but not `display: none`, which screen
   readers skip; the same block as .su-live and .fb-live. */
.yt-scope .yt-live {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}
/* --------------------------------------------------------------------------
   What was found. The old screen showed nothing at all between the paste box
   and the download: no title, no length, no confirmation that the link even
   pointed at a real video, so a typo in the id was only discovered as a failure
   after the wait. This panel is the answer to "is this the right video".

   The thumbnail is the one third-party request the page makes. It comes from
   i.ytimg.com, built by conversion.youtube.thumbnail_url from the id rather
   than read out of the extractor payload, so nothing yt-dlp returns can point
   this <img> anywhere else. It carries referrerpolicy="no-referrer" and the
   frame keeps its 16/9 box whether the image loads, is blocked, or 404s.
   -------------------------------------------------------------------------- */
.yt-scope .yt-video {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  gap: 14px;
  align-items: start;
  padding-bottom: 16px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--yt-border);
}

.yt-scope .yt-thumb {
  position: relative;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--yt-border);
  border-radius: var(--yt-radius-sm);
  background: var(--yt-surface-sunken);
  overflow: hidden;
}

.yt-scope .yt-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Before the first inspect the <img> has no src; an empty src would make some
   browsers re-request the page itself, so the template leaves the attribute
   off and the script sets it. Nothing to hide — the panel above is hidden. */
.yt-scope .yt-thumb img:not([src]) { display: none; }
.yt-scope .yt-meta__title {
  margin: 0;
  font-family: var(--yt-font-display);
  font-size: 15px;
  font-weight: 700;
  line-height: 22px;
  color: var(--yt-text) !important;
  /* A YouTube title can be 100 characters. Three lines, then an ellipsis. */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.yt-scope .yt-meta__rows {
  display: flex;
  align-items: center;
  gap: 6px 14px;
  flex-wrap: wrap;
  margin: 8px 0 0;
}

.yt-scope .yt-fact {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  line-height: 18px;
  color: var(--yt-text-soft) !important;
}

.yt-scope .yt-fact i { font-size: 13px; color: var(--yt-text-muted); }

.yt-scope .yt-meta__link {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-top: 10px;
  font-family: var(--yt-font-display);
  font-size: 12px;
  font-weight: 700;
  line-height: 18px;
  text-decoration: none;
  color: var(--yt-brand) !important;
}

.yt-scope .yt-meta__link:hover,
.yt-scope .yt-meta__link:focus {
  color: var(--yt-brand-hover) !important;
  text-decoration: underline;
}
/* --------------------------------------------------------------------------
   The quality picker. The reason the screen is being rewritten at all: pytube
   was called with `progressive=True`, and YouTube only muxes sound into the
   picture up to 720p, so "various resolutions" in the page's own description
   was never true — every download came out at 720p or below, whatever the
   upload actually offered.

   Now conversion.youtube.offers_for reads the heights the upload really has and
   the script builds one card per height plus an MP3 row. Cards, not a <select>:
   the file size belongs beside the choice, and a select cannot show it.

   The native radio stays in the DOM, focusable and reachable by arrow keys —
   `opacity: 0`, not `display: none`, which would take it out of the tab order
   and out of the accessibility tree. The card is styled off `:checked` and
   `:focus-visible` on that input.
   -------------------------------------------------------------------------- */
.yt-scope .yt-offers {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
  gap: 8px;
}

.yt-scope .yt-offer {
  position: relative;
  display: block;
  margin: 0;
  padding: 0;
  font-weight: 400;
  cursor: pointer;
}

.yt-scope .yt-offer__radio {
  position: absolute;
  top: 12px;
  left: 12px;
  width: 16px;
  height: 16px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.yt-scope .yt-offer__body {
  display: grid;
  grid-template-columns: 30px minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
  height: 100%;
  padding: 9px 11px;
  border: 1px solid var(--yt-border-strong);
  border-radius: var(--yt-radius-md);
  background: var(--yt-surface);
  transition: border-color 0.15s ease, background-color 0.15s ease,
    box-shadow 0.15s ease;
}

.yt-scope .yt-offer:hover .yt-offer__body { border-color: var(--yt-text-muted); }

.yt-scope .yt-offer__radio:checked + .yt-offer__body {
  border-color: var(--yt-brand);
  background: var(--yt-brand-bg);
  box-shadow: inset 0 0 0 1px var(--yt-brand);
}

.yt-scope .yt-offer__radio:focus-visible + .yt-offer__body {
  box-shadow: 0 0 0 3px var(--yt-brand-bg), inset 0 0 0 1px var(--yt-brand);
  border-color: var(--yt-brand);
}

.yt-scope .yt-offer__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--yt-radius-sm);
  background: var(--yt-surface-sunken);
  color: var(--yt-text-soft);
}

.yt-scope .yt-offer__icon i { font-size: 15px; }

.yt-scope .yt-offer__radio:checked + .yt-offer__body .yt-offer__icon {
  background: var(--yt-brand);
  color: #ffffff;
}
.yt-scope .yt-offer__text { min-width: 0; }

.yt-scope .yt-offer__label {
  display: block;
  font-family: var(--yt-font-display);
  font-size: 13px;
  font-weight: 700;
  line-height: 19px;
  color: var(--yt-text);
}

.yt-scope .yt-offer__note {
  display: block;
  font-size: 11px;
  line-height: 16px;
  color: var(--yt-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.yt-scope .yt-offer__size {
  font-family: var(--yt-font-mono);
  font-size: 11px;
  line-height: 16px;
  white-space: nowrap;
  color: var(--yt-text-soft);
}

.yt-scope .yt-offer__radio:checked + .yt-offer__body .yt-offer__size {
  color: var(--yt-brand);
}

/* The row under the picker: the download button, and the note about what
   happens next. */
.yt-scope .yt-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--yt-border);
}

.yt-scope .yt-actions__note {
  margin: 0;
  font-size: 12px;
  line-height: 18px;
  color: var(--yt-text-muted) !important;
}
/* A count beside a card title — "4 qualities". */
.yt-scope .yt-chip {
  display: inline-flex;
  align-items: center;
  height: 20px;
  padding: 0 8px;
  border: 1px solid var(--yt-border-strong);
  border-radius: 10px;
  background: var(--yt-surface);
  font-family: var(--yt-font-display);
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
  color: var(--yt-text-soft);
}

.yt-scope .yt-chip[hidden] { display: none; }

/* --------------------------------------------------------------------------
   Waiting. Two different waits, and the old screen showed neither: the second
   or two while the server asks YouTube what a link holds, and the much longer
   stretch while yt-dlp pulls the picture and the sound and ffmpeg merges them.
   During the second one the browser is sitting on an unanswered POST with no
   spinner of its own, which is exactly when a person clicks again.
   -------------------------------------------------------------------------- */
.yt-scope .yt-spin {
  display: inline-block;
  width: 13px;
  height: 13px;
  flex: none;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: yt-spin 0.7s linear infinite;
}

@keyframes yt-spin { to { transform: rotate(360deg); } }
.yt-scope .yt-prep {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 14px;
  align-items: center;
  margin-top: 16px;
  padding: 16px;
  border: 1px solid var(--yt-brand);
  border-radius: var(--yt-radius-md);
  background: var(--yt-brand-bg);
}

.yt-scope .yt-prep[hidden] { display: none; }

/* Three bars, because this is a media tool and a ring would say nothing about
   what is being waited on. Decorative: aria-hidden in the markup. */
.yt-scope .yt-prep__bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  width: 34px;
  height: 26px;
}

.yt-scope .yt-prep__bars span {
  flex: 1;
  height: 100%;
  border-radius: 2px;
  background: var(--yt-brand);
  transform-origin: bottom center;
  animation: yt-bars 1.1s ease-in-out infinite;
}

.yt-scope .yt-prep__bars span:nth-child(2) { animation-delay: 0.18s; }
.yt-scope .yt-prep__bars span:nth-child(3) { animation-delay: 0.36s; }

@keyframes yt-bars {
  0%, 100% { transform: scaleY(0.35); }
  50%      { transform: scaleY(1); }
}
.yt-scope .yt-prep__title {
  margin: 0;
  font-family: var(--yt-font-display);
  font-size: 13px;
  font-weight: 700;
  line-height: 20px;
  color: var(--yt-brand) !important;
}

.yt-scope .yt-prep__note {
  margin: 3px 0 0;
  font-size: 12px;
  line-height: 18px;
  color: var(--yt-text-soft) !important;
}

/* Someone who has asked their system to stop animating things gets the panel
   and the sentence, with the bars held still rather than removed — the panel is
   the message; the motion was only ever decoration. */
@media (prefers-reduced-motion: reduce) {
  .yt-scope .yt-prep__bars span,
  .yt-scope .yt-spin {
    animation: none;
  }

  .yt-scope .yt-prep__bars span { transform: scaleY(0.7); }

  .yt-scope .yt-btn,
  .yt-scope .yt-offer__body {
    transition: none;
  }
}
/* --------------------------------------------------------------------------
   The house rules. Every limit the server enforces is written here, because a
   limit a person only meets as a refusal reads as a bug. The numbers come from
   settings (YOUTUBE_MAX_DURATION_SECONDS, YOUTUBE_DAILY_DOWNLOAD_LIMIT) through
   the view, never typed into the markup.
   -------------------------------------------------------------------------- */
.yt-scope .yt-notes { margin-top: 16px; }

.yt-scope .yt-notes__list {
  display: grid;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.yt-scope .yt-notes__list li {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr);
  gap: 9px;
  align-items: start;
  font-size: 12px;
  line-height: 18px;
  color: var(--yt-text-soft);
}

.yt-scope .yt-notes__list li i {
  margin-top: 1px;
  font-size: 14px;
  color: var(--yt-brand);
}

.yt-scope .yt-notes__list strong {
  font-family: var(--yt-font-display);
  font-weight: 700;
  color: var(--yt-text);
}

/* --------------------------------------------------------------------------
   Without JavaScript. The form still posts and still downloads — the picker is
   what needs the script, and a blank `choice` falls through to
   conversion.youtube.DEFAULT_CHOICE. This says so instead of leaving a Check
   button that does nothing.
   -------------------------------------------------------------------------- */
.yt-scope .yt-noscript {
  margin: 0 0 16px;
  padding: 11px 14px;
  border: 1px solid var(--yt-warn-border);
  border-radius: var(--yt-radius-sm);
  background: var(--yt-warn-bg);
  font-size: 12px;
  line-height: 19px;
  color: var(--yt-warn) !important;
}

/* The same point, made where the picker would be. It sits in the markup so that
   a page with no working script still explains its empty quality grid, and
   youtube.js adds `is-ready` to the wrapper as its first act, which takes it
   away — one mechanism, driven by the script being alive rather than by any
   later step succeeding. */
.yt-scope .yt-fallback {
  margin: 0;
  font-size: 12px;
  line-height: 18px;
  color: var(--yt-text-soft) !important;
}

.yt-scope.is-ready .yt-fallback { display: none; }
/* Insurance. mdb.min.css and the theme both set `display` on enough element
   types that a plain `[hidden]` can lose; every panel this screen shows and
   hides is driven by the attribute, so it has to hold. */
.yt-scope [hidden] { display: none !important; }

/* --------------------------------------------------------------------------
   Narrow screens. The dashboard column is already inside .col-lg-9, so these
   widths are the viewport, not the content box — the thumbnail row is the first
   thing to give at 720px and the paste row at 560px.
   -------------------------------------------------------------------------- */
@media (max-width: 767px) {
  .yt-scope .yt-video {
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
  }

  .yt-scope .yt-thumb { max-width: 320px; }
  .yt-scope .yt-title { font-size: 19px; line-height: 26px; }
  .yt-scope .yt-offers { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 559px) {
  .yt-scope .yt-paste { grid-template-columns: minmax(0, 1fr); }
  .yt-scope .yt-paste .yt-btn { width: 100%; }
  .yt-scope .yt-card__body { padding: 14px; }
  .yt-scope .yt-actions .yt-btn { width: 100%; }
  .yt-scope .yt-thumb { max-width: none; }
}
