canvas {
  touch-action: none !important;
}
#root .do-next-block header.commonRed_Head {
  margin-bottom: 20px;
}

.path-channel-report #block-reactdonextblock-2 {
  flex: 0 49%;
  margin: 0;
  margin-right: 10px;
}

.hide-acc-status {
  display: none;
}

.video-report-page-wrapper
  #root
  .panel.panel-default.report-boxes
  .do-next_item
  .skip_step {
  margin-left: 6px;
}
.dashboard .gear-setting {
  background: #fff;
}
.dashboard .gear-setting:hover {
    background-color: var(--secondary-color);
 }
@media (min-width: 768px) {
  .view-toolbox-dashboard .gear-setting-wrapper .title {
    width: 80%;
  }
}
.dashboard .view-toolbox-dashboard .swiper .swiper-button-prev,
.dashboard .view-toolbox-dashboard .swiper .swiper-button-next {
  display: none;
}

.dashboard .view-toolbox-dashboard .swiper-button-prev.swiper-button-disabled,
.dashboard .view-toolbox-dashboard .swiper-button-next.swiper-button-disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}


@keyframes dots {
  0%,20%  { content: ".";   }
  40%     { content: "..";  }
  60%,100%{ content: "..."; }
}

.dashboard .dots-loader::after {
  content: ".";
  animation: dots 1.2s steps(1, end) infinite;
  display: inline-block;
  width: 1.2em;
  text-align: left;
}


/* React social post page updates start*/

.node-social-post-edit-form .expire-token-modal .react-modal-body.modal-body img,
.node-social-post-form .expire-token-modal .react-modal-body.modal-body img {
  height: 50px;
}

.custom-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(0px);
  z-index: 1040;
}

.change-timezone-btn {
  padding: 10px;
  width: 100%;
  max-width: 300px;
  display: block;
  background-color: #6a1b9a;
  color: white;
  border: none;
  border-radius: 8px;
  font-size: 14px;
  text-align: center;
  cursor: pointer;
  box-sizing: border-box;
}

/* Fix for cross-browser width */
.change-timezone-btn {
  width: -webkit-fill-available;
  /* Chrome and Safari */
  width: -moz-available;
  /* Firefox */
  width: fill-available;
  /* Standard */
}

/* React social post page updates end*/

/* add-social-post page css start*/

#add-social-post .control-field .form-label {
  margin-bottom: 8px;
}

#add-social-post .form-label-wrapper {
  display: flex;
  justify-content: space-between;
}

#add-social-post .control-field textarea.form-control .post-something-textarea {
  min-height: 220px;
}

#add-social-post .control-field textarea.form-control::placeholder {
  color: #b1b7c5;
  font-size: 12px;
  font-weight: 500;
}

#add-social-post .post-contents small.text-small {
  color: var(--paragraph, #6b6880);
  font-size: 12px;
  font-weight: 500;
}

#add-social-post .post-preview #postPreviewTabs {
  border: unset;
  gap: 6.8px;
  margin-bottom: -1px;
}

#add-social-post .post-preview #postPreviewTabs .nav-link {
  background: var(--primary-light);
  border: unset;
  border-radius: 6px 6px 0 0;
  font-size: 12px;
  font-weight: 500;
  color: var(--title);
  opacity: 0.6;
}

#add-social-post .post-preview #postPreviewTabs .nav-link.active {
  opacity: 1;
  font-weight: 700;
}

#add-social-post .post-preview .tab-content {
  background: #f2eef8;
  padding: 16px;
  padding-bottom: 5px;
}

#add-social-post
  .post-preview
  #postPreviewTabs
  li:first-child
  #postPreviewTabs-tab-post-preview.active {
  background: unset;
}

#add-social-post
  .post-preview
  #postPreviewTabs
  #postPreviewTabs-tab-post-preview.active {
  background: unset;
}

#add-social-post .post-preview #postPreviewTabs .nav-link {
  border: 1px solid transparent;
}

#add-social-post span.variation-count {
  font-size: 14px;
  font-weight: bold;
  color: var(--title);
}

#add-social-post .links-attachments {
  margin-top: 8px;
}

#add-social-post .control-field.timezone input {
  border-radius: 8px 0 0 8px;
  font-size: 14px;
  color: var(--paragraph, #6b6880);
  font-weight: bold;
}

#add-social-post .control-field.timezone .d-flex {
  position: relative;
}

#add-social-post .control-field.timezone .change-timezone-btn {
  position: absolute;
  top: 50%;
  right: 3px;
  max-width: 100px;
  padding: 4px;
  font-size: 10px;
  font-weight: bold;
  height: 34px;
  transform: translateY(-50%);
  border-radius: 6px;
}

#add-social-post .social-common-btn {
  padding: 10px 7px;
  background: #fdeeea;
  min-width: 150px;
  color: var(--title);
  font-weight: bold;
  margin-bottom: 0;
  border-radius: 8px;
}

#add-social-post .social-account-wrapper .btn-active {
  background: green;
  color: #ffffff;
}

#add-social-post .action-btn button {
  min-width: 150px;
}

#add-social-post .spinner-border {
  width: 15px;
  height: 15px;
}

#add-social-post .social-account-section > label {
  margin-bottom: 0.25rem;
  color: var(--title);
  font-weight: 500;
}

#add-social-post .preview-image-wrapper img.preview-image {
  max-width: 80px;
  width: 100%;
  height: 30px;
  object-fit: contain;
}

#add-social-post .preview-image-wrapper {
  margin-top: 8px;
}

#add-social-post .user-info-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

#add-social-post .social-btn-wrapper a[type="button"] {
  text-transform: uppercase;
  font-size: 12px;
  color: var(--title);
  font-weight: bold;
}

#add-social-post .social-account-wrapper .disable-btn {
  background: #ea2e2e;
  color: #ffffff;
  pointer-events: none;
}

#add-social-post .form-check-input:disabled ~ .form-check-label,
#add-social-post .form-check-input[disabled] ~ .form-check-label {
  opacity: 1;
}

#add-social-post .reconnect-link {
  flex: unset;
}

#add-social-post .expire-token-modal .madal-popup h4 {
  font-size: 16px;
}

#add-social-post .madal-popup p {
  font-size: 13px;
}

#add-social-post .madal-popup .modal-footer {
  justify-content: space-between;
}

#add-social-post .expire-token-modal .modal-card .img-figure img {
  max-width: 250px;
  width: 100%;
  height: 50px;
  object-fit: contain;
}

#add-social-post .timezone-popup .modal-footer .btn {
  flex: unset;
}

#add-social-post .Toastify__toast {
  justify-content: flex-start;
}

#add-social-post .timezone-popup .form-label {
  font-weight: 700;
  font-size: 14px;
}

/* add-social-post page css end*/

.autoposter-modal .container-fluid,
.autoposter-modal .social-post-text-preview {
  padding: 0;
}

.autoposter-modal .modal-body,
.react-modal-body.modal-body {
  padding: 16px;
}

.autoposter-modal .form-label-wrapper {
  display: flex;
  justify-content: space-between;
}

.autoposter-modal .modal-footer .btn {
  flex: unset;
}

.autoposter-modal .modal-footer {
  justify-content: flex-start;
  padding: 0;
}

.rmdp-calendar {
  background: #f2eef8;
}

.rmdp-shadow {
  box-shadow: none !important;
}

.rmdp-calendar {
  border-radius: 5px;
}

.rmdp-calendar {
  border-radius: 5px;
}

.rmdp-day,
.rmdp-week-day {
  width: 32px !important;
  height: 32px !important;
  margin: 2px;
  background: #fff;
  border-radius: 3px;
}

.rmdp-day.rmdp-day-hidden {
  background: unset;
}

.rmdp-day-picker .rmdp-week:first-child .rmdp-week-day {
  background: #f2eef7;
  font-weight: 600;
  color: #000000;
}

.rmdp-day.rmdp-today span {
  border-radius: 3px !important;
  padding: 0 !important;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
}

.rmdp-day:not(.rmdp-disabled, .rmdp-day-hidden) span:hover {
  background-color: #fff !important;
  color: #000 !important;
  border: 0;
}

.rmdp-day.rmdp-selected span:not(.highlight) {
  background-color: #fff;
  color: #000;
  border: 0;
}

.post-schedule h4 {
  font-size: 14px;
  margin-bottom: 8px;
  color: #3e2155;
}

.post-schedule ul {
  background: #f2eef8;
  border-radius: 5px;
  max-height: 314px;
  overflow: auto;
  padding: 8px;
}

.post-schedule li {
  margin: 0;
  padding: 0;
  border: unset;
  color: #000;
  font-size: 12px;
}

.post-schedule li small {
  flex: 0 100%;
  text-align: right;
}

.rmdp-day span {
  top: 0 !important;
  left: 0 !important;
  bottom: 0 !important;
  right: 0 !important;
}

.rmdp-day.rmdp-selected span:not(.highlight) {
  border-radius: 3px !important;
  background: #fff;
  color: #000;
  box-shadow: unset !important;
}

.rmdp-day.rmdp-selected {
  border-radius: 0 !important;
}

span.sd {
  border-radius: 0 !important;
  padding: 0 !important;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
}

.commonLabel {
  font-size: 14px;
  font-weight: 700;
  color: var(--title);
  margin-bottom: 6px;
}

span.hBox {
  width: 24px;
  height: 24px;
  display: inline-block;
  border-radius: 3px;
}

.highlightBox {
  display: flex;
  padding-top: 10px;
  flex-wrap: wrap;
  justify-content: space-around;
}

span.hBox.hBoxGreen {
  background: green;
}

span.hBox.hBoxRed {
  background: #ea2e2e;
}

.hbList {
  display: flex;
  align-items: center;
  gap: 8px;
}

.hbList span.text-black {
  font-size: 10px;
  color: #000;
  font-weight: 500;
}

.warning-container {
  padding: 8px;
  background: #f2eef8;
  margin-bottom: 12px;
  border-radius: 5px;
}

.blankLabel {
  display: block;
}

.text-message {
  position: absolute;
  top: 55%;
  left: 49%;
  color: #f2eef8;
  font-size: 18px;
  transform: translate(-50%, -50%);
  margin-top: 25px;
  margin-left: 20px;
  font-weight: bold;
  text-transform: uppercase;
}

.rmdp-day.rmdp-today span {
  background-color: #fff !important;
  color: #000 !important;
  border-radius: 0px !important;
}

.global-dotted-loader {
  width: 42px;
  aspect-ratio: 4;
  --_g: no-repeat radial-gradient(circle closest-side, #763475 90%, #0000);
  background:
    var(--_g) 0% 50%,
    var(--_g) 50% 50%,
    var(--_g) 100% 50%;
  background-size: calc(100% / 3) 100%;
  animation: l7 1s infinite linear;
}

.global-dotted-loader.sidebar-dots {
  width: 30px;
}

.loader {
  width: 42px;
  aspect-ratio: 4;
  --_g: no-repeat radial-gradient(circle closest-side, #763475 90%, #0000);
  background:
    var(--_g) 0% 50%,
    var(--_g) 50% 50%,
    var(--_g) 100% 50%;
  background-size: calc(100% / 3) 100%;
  animation: l7 1s infinite linear;
}

.emoji-picker-container {
  position: relative;
}

.emoji-picker-button {
  position: absolute;
  font-size: 20px;
  background: none;
  border: none;
  bottom: 45px;
  right: 25px;
  width: 32px;
  height: 32px;
}

.emoji-picker-header {
  cursor: grab;
  color: white;
  background: var(--primary-color);
  padding: 5px;
  text-align: center;
  border-top-left-radius: 8px;
  border-top-right-radius: 8px;
}

#emoji-picker-wrapper {
  position: fixed;
  z-index: 999999999999;
  width: fit-content;
  height: fit-content;
  background: white;
  border-radius: 8px;
  box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2);
  padding: 5px;
  transform: translate(0px, 0px);
}

.emoji-picker-btn {
  position: absolute;
  bottom: 0px;
  right: 0px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-size: 24px;
}

.emoji-picker-btn:hover {
  opacity: 0.8;
}

.preview-image-wrapper {
  display: flex;
  align-items: center;
  max-width: 100%;
  justify-content: space-between;
}

.preview-image {
  width: 50px;
  /* Adjust image size */
  height: 50px;
  object-fit: cover;
  border-radius: 4px;
}

.preview-image-name {
  flex-grow: 1;
  /* Allows name to take available space */
  font-size: 14px;
  color: #333;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.btn-remove-photo {
  background: transparent;
  border: none;
  color: red;
}

.remove-icon {
  width: 16px;
  height: 16px;
}

@keyframes l7 {
  33% {
    background-size:
      calc(100% / 3) 0%,
      calc(100% / 3) 100%,
      calc(100% / 3) 100%;
  }

  50% {
    background-size:
      calc(100% / 3) 100%,
      calc(100% / 3) 0%,
      calc(100% / 3) 100%;
  }

  66% {
    background-size:
      calc(100% / 3) 100%,
      calc(100% / 3) 100%,
      calc(100% / 3) 0%;
  }
}

.global-dotted-loader.loader-on-field {
  position: absolute;
  top: 15px;
  left: 15px;
}

.btnWrapper .loader {
  position: absolute;
  width: 45px;
  top: 35%;
  left: 50%;
  transform: translate(-50%, -50%);
  --_g: no-repeat radial-gradient(circle closest-side, #000000 90%, #0000);
}

.btnWrapper {
  position: relative;
}
.on-image-loader {
  position: absolute;
  top: 86px;
  left: 137px;
}
.thumbnail-blur {
  filter: blur(3px);
  opacity: 0.3;
}
.cursor-pointer {
  cursor: pointer;
}
/* Disabled buttons.
 *
 * Group opacity works for an outlined or ghost button and is actively wrong
 * for a filled one. `opacity` flattens the WHOLE rendered element toward the
 * page: the purple fill lifts to a pale lilac while the white label stays
 * white, so the label ends up at 2.36:1 against its own button and cannot be
 * read at all. The Generate script modal is where this shows worst, because
 * the disabled control is the primary action and the user needs to know what
 * it will do before they can enable it.
 *
 * This is not a repaint regression. The old purple flattens to #bfa1c3 and
 * the new one to #bba0c0, so both measure about 2.3 and both were always
 * illegible. The repaint just put it in front of someone.
 *
 * Disabled controls are exempt from WCAG 1.4.3, which is why this survived a
 * contrast sweep. Exempt from the rule is not the same as fit to read.
 */
.btn.disabled,
.btn:disabled,
fieldset:disabled .btn {
  pointer-events: none;
  opacity: 0.45;
}

/* Filled roles opt out of the opacity and state their disabled colours, so
   the label stays legible instead of dissolving into its own fill.
   #5b5872 on #e9e1ec measures 5.33:1. */
.btn.btn-primary:disabled,
.btn.btn-primary.disabled,
.btn.btn-secondary:disabled,
.btn.btn-secondary.disabled,
.btn.btn-fill:disabled,
.btn.btn-fill.disabled,
fieldset:disabled .btn.btn-primary,
fieldset:disabled .btn.btn-secondary {
  opacity: 1;
  background: #e9e1ec;
  border-color: #d6cbda;
  color: #5b5872;
}

.action-btn-group {
  margin-top: 12px;
}

.autoposter-modal .rmdp-arrow-container {
  background: #feefe8;
  border-color: #feefe8;
  width: 30px;
  height: 30px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.autoposter-modal .warning-container .caution {
  color: var(--secondary-color);
  font-weight: 500;
}

.autoposter-modal .warning-container .warning {
  font-weight: 500;
}

.social-links .btmText {
  margin-top: -15px;
}

.video-preview {
  height: auto;
}

.post-preview .user-image img {
  width: 35px !important;
}

.post-preview .wrapper-preview {
  background: #f2eef8;
}

.post-preview .preview-text,
.post-preview .preview-link {
  padding-left: 15px;
  padding-right: 15px;
  padding-bottom: 10px;
}

.post-preview .twitter {
  padding-left: 59px;
}

.post-preview .twitter .preview-text {
  margin-top: 0px;
}

.post-preview .twitter .preview-text,
.post-preview .twitter .preview-link {
  padding-left: 0;
}

.post-preview .title-date-wrapper.head-twitter .preview-post-date {
  display: inline-block;
  padding-left: 5px;
}
.post-preview .title-date-wrapper.head-bluesky .preview-post-date {
  display: inline-block;
  padding-left: 5px;
}
.post-preview .twitter .video-preview {
  border-radius: 20px;
  width: 100%;
}

.post-preview .linkedin .video-preview {
  border-radius: 5px;
  width: 100%;
}

.post-preview .facebook_page .video-preview {
  border-radius: 0;
  width: 100%;
}

.post-preview .facebook_group .video-preview {
  border-radius: 0;
  width: 100%;
}

.autoposter-modal .rmdp-arrow-container i {
  border-color: #f26419;
  margin: 0 0 0 0px;
  padding: 3px;
}

.autoposter-modal .rmdp-arrow-container:hover {
  background: #f26419;
}

.form-check-input.social-media-checkbox {
  height: 17px;
  width: 17px;
  border: 2px solid var(--primary-color);
}

.form-check-input.social-media-checkbox:checked {
  background-color: var(--primary-color);
  border: 2px solid var(--primary-color);
}

.post-contents.post-preview .loader.global-dotted-loader {
  position: absolute;
  top: 19px;
  left: 130px;
}

#add-social-post .modal.fade.show.d-block.expire-token-modal .modal-footer {
  display: flex;
  justify-content: space-between;
}

.post-schedule i.bi.bi-calendar3 {
  color: red;
}

.post-schedule i.bi.bi-trash {
  font-size: 16px;
}

.autoposter-modal .post-schedule {
  min-height: 324px;
  background: #f2eef8;
  border-radius: 5px;
}

.form-check-input.error-box {
  display: inline-block;
  border: 2px solid red;
  animation-name: blinking;
  animation-duration: 1s;
  animation-iteration-count: 100;
}

.on-hover-date {
  border-radius: 3px;
  opacity: 0.78;
  padding: 1px;
  background: rgb(217, 217, 217);
  border: 1px solid var(--primary-color);
}

#add-social-post .cursor-pointer {
  cursor: pointer;
}

.social-links .autoposter-modal li {
  margin-bottom: unset;
}

.post-contents .btnWrapper .btn-primary.convert-to-spintax-btn {
  font-size: 9px;
}

#add-social-post .post-preview-section {
  word-break: break-word;
}

.thumbnail-preview {
  top: 0;
  right: -160px;
  width: 150px;
  height: 100px;
  border: 1px solid #ccc;
  border-radius: 5px;
  z-index: 10;
}

@keyframes blinking {
  50% {
    border-color: #ffcd5f;
  }
}

.social-media-checkbox {
  margin: 0;
}

.more-variations-wrapper {
  display: flex;
  gap: 6px;
}

.social-post-text-preview .more-variations-wrapper .number-of-variations {
  width: 60px;
  display: flex;
  flex-direction: column-reverse;
  margin-bottom: 0;
}

.more-variations-wrapper .number-of-variations select {
  width: 100%;
  text-align: center;
}

.more-variations-wrapper {
  display: flex;
  gap: 10px;
}

.button_autoBox {
  gap: 72px;
}

.button_autoBox .number-of-variations {
  width: 60px;
  margin-bottom: 0;
}

.social-post-text-preview .action-btn-group {
  display: flex;
  justify-content: flex-start;
  gap: 16px 72px;
  flex-wrap: wrap;
}

.variations-title {
  padding: 2px;
}

/*SEO react page start*/
.seo-page-form .search-history {
  background: var(--primary-light);
  z-index: 1000;
  overflow-y: scroll;
}

.seo-page-form .search-history li {
  display: block;
  padding: 5px;
  border-bottom: 1px solid;
  cursor: pointer;
}

.keyword-badge.custom-labels {
  font-size: 12px;
  font-weight: 600;
  line-height: 16px;
  border-radius: 4px;
  margin-bottom: 5px;
}

.keyword-card .sub-heading {
  color: var(--primary-color);
}

.keyword-tab-block-body.card-body {
  background: #f2eef8;
  border-radius: 0px 7px 7px 7px;
}

ul.seo-tools-tabs {
  gap: 4px;
}

/* Tabs share a baseline and only the active tab fills with the content
 * area colour (white). Previous design had the inactive tabs floating
 * 4px above content and the active tab filled with lavender, which
 * matched the page background and made the tab look sunken rather
 * than connected to its content area. */
.tools-tabs .nav-link:not(.active),
.keyword-tab-block .nav-link:not(.active) {
  margin: 0;
  background: transparent;
  border-radius: 6px 6px 0 0;
  padding: 8px 16px;
  /* #858b98 measured 3.42:1 here, below the 4.5 a tab label needs. An inactive
     tab should recede, but receding is a job for weight and fill, not for
     making the word hard to read. #5b5872 is 6.81:1 and still clearly the
     quieter of the two. */
  color: #5b5872;
  font-weight: 500;
  border: 0;
  min-width: 128px;
  position: relative;
  z-index: 3;
}

.tools-tabs .nav-link.active,
.keyword-tab-block .nav-link.active {
  background: #fff;
  border-radius: 6px 6px 0 0;
  border: 0;
  color: #3e2155;
  font-weight: 600;
  padding: 8px 16px;
}

.tools-tabs.nav-tabs,
.keyword-tab-block .nav-tabs {
  border-bottom: 0;
}

.tools-tabs .nav-link:not(.active):hover {
  opacity: unset;
  color: #3e2155;
}

/* GET VIDEO OUTLIERS intro banner on the Homerun finder, matching the
   extension (TS-5107). */
.homerun-outliers-banner {
  background: #f3effa;
  border-radius: 12px;
  padding: 16px 20px;
  margin: 0 0 16px;
}
.homerun-outliers-banner__head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 4px;
}
.homerun-outliers-banner__icon {
  color: var(--primary-color, #672c72);
  font-size: 18px;
  flex-shrink: 0;
}
.homerun-outliers-banner__title {
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--text-color, #1f2937);
  letter-spacing: 0.02em;
}
.homerun-outliers-banner__sub {
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
}

/* Total-records header shown once the outlier API returns results (TS-5107). */
.homerun-results-count {
  font-weight: 700;
  font-size: 0.95rem;
  color: var(--text-color, #1f2937);
  margin: 4px 0 8px;
}

/* Hide the floating help toggle when the page has no right sidebar for it to
   open - the button is a dead control otherwise (TS-5107). Keyed on the right
   sidebar's presence so it is correct regardless of which template renders the
   toggle. */
body:not(:has(.column-box-right-side-bar)) .header-accordian-toggle-btn {
  display: none;
}

.fs-14 {
  font-size: 14px;
}

.fs-12 {
  font-size: 12px;
}

html[style*="overflow: hidden"] {
  padding-right: 0 !important;
}

body[style*="overflow: hidden"] {
  padding-right: 0 !important;
}

a.round-icon {
  display: block;
  border-radius: 50%;
  background: #f2eef8;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  aspect-ratio: 1/1;
}

a.round-icon i {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  aspect-ratio: 1/1;
  color: var(--primary-color);
  position: relative;
  left: 3px;
  top: 1px;
}

.small-button {
  background: #f8f5f8;
  color: var(--primary-color);
}

.small-button:hover {
  color: #f8f5f8;
  background: var(--primary-color);
}

.badge-green {
  background-color: #2bc485 !important;
}

.badge-yellow {
  background-color: #aedd2d !important;
}

.badge-orange {
  background-color: #f48448 !important;
}

.badge-red {
  background-color: #ea2e2e !important;
}

.badge-color {
  position: absolute;
  top: 5px;
  left: 6px;
  font-size: 0.8rem;
  display: inline-flex;
  padding: 5px 8px;
  justify-content: center;
  align-items: center;
  gap: 8px;
}

/* Competitor page two-column layout */
.competitors-layout {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  min-height: 0;
  /* One white body for the whole Competitor admin tab, joining flush under the
     tab strip (like the Viewers page / HomeRun), rather than separate white
     cards for the sidebar and the video panel. */
  background: #fff;
  border-radius: 0 8px 8px 8px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
/* Flatten the video panel Card so it sits on the shared white body. */
.competitors-layout .cv-panel.card {
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0 !important;
}
.competitors-layout__main {
  flex: 1;
  min-width: 0;
}

/* ── Competitor video panel ──────────────────────────────────────────── */
.cv-panel {
  flex: 1;
  min-width: 0;
  overflow: visible !important;
}
.cv-panel__toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.cv-panel__filters {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.cv-panel__count {
  margin-left: auto;
  font-size: 0.82rem;
  color: var(--text-muted, #6c757d);
}
.cv-panel__hint {
  font-size: 0.76rem;
  color: var(--text-muted, #6c757d);
  margin: 0 0 10px 2px;
}
.cv-panel__list {
  display: flex;
  flex-direction: column;
  /* Bordered cards need room to breathe (TS-5070 item 2). */
  gap: 8px;
  max-height: 70vh;
  overflow-y: auto;
}
.cv-panel__empty {
  padding: 48px 16px;
  text-align: center;
  color: var(--text-muted, #6c757d);
  font-size: 0.9rem;
}
.cv-panel__empty-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 6px;
}
.cv-panel__empty-sub {
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
}

/* Viewers v2 - Who's watching (TS-5115). */
/* The whole viewers tab content sits on one white card body, joining flush under
   the active (left-most) tab. */
.viewers-page .tab-content {
  background: #fff;
  border-radius: 0 8px 8px 8px;
  padding: 20px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
/* The build form is flat, since the tab body is the card, and it carries NO
   card chrome rules on purpose: it is a plain div rather than a Bootstrap
   .card (TS-5507). It used to be a .card flattened from here, which could not
   work - `body.ts-glass-page .card` in glass.css is (0,2,1) and loads later,
   so every declaration here lost except the one with !important on it. The
   result was a frosted panel drawn inside the white tab card. Not being a
   .card is the version that cannot be re-broken by the next glass rule. */
/* The match banner is a bordered card at the top of both tabs (extension 7.5.1),
   its border tinted by the match tone. */
.viewers-match-card {
  background: #f7f6f9;
  border: 1px solid var(--border-color, #e5e7eb);
  border-radius: 8px;
  box-shadow: none;
}
/* Tone tints, at (0,3,0) so they beat `body.ts-glass-page .card` (0,2,1),
   whose `border` shorthand resets border-color and left every tone the same
   neutral (TS-5507). Only the colour is restated: the 1px solid from glass is
   wanted, it is the tint that was lost. Same arithmetic guided-help.css:413
   documents against the same selector. */
.viewers-page .tab-content .viewers-match-card--ok { border-color: #1c7a45; }
.viewers-page .tab-content .viewers-match-card--warn { border-color: #9a6a13; }
.viewers-page .tab-content .viewers-match-card--far { border-color: #a13333; }
/* Explorer rows read as light-purple list items on the white body, matching the
   house form fields (--primary-light). */
.viewers-explorer__row {
  background: var(--primary-light, #f2eef8);
  border: 1px solid var(--primary-light, #f2eef8);
  box-shadow: none;
}
/* Rename/Rebuild prompt body inside the standard modal. */
.viewers-prompt__label {
  display: block;
  font-size: 0.88rem;
  margin-bottom: 8px;
  color: var(--text-color, #1f2937);
}
.viewers-prompt__input {
  width: 100%;
  margin-bottom: 16px;
}
.viewers-prompt__actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
/* The tab-to-body join is handled by .viewers-page .tab-content squaring its
   own top-left corner. There used to be a
   `.viewers-watching > .card:first-child { border-top-left-radius: 0 }` here as
   well, to square the inner card, which at (0,3,0) was quietly the one rule
   holding back glass's 14px radius. With the build form no longer a .card
   there is no inner corner left to square (TS-5507). */
/* Explorer action buttons use the house primary/secondary styles; keep them
   compact so a row of them stays tidy. */
.viewers-explorer__row-actions .btn.btn-primary {
  padding: 6px 14px;
  font-size: 0.82rem;
}
/* Delete keeps the secondary shape but reads as destructive. */
.viewers-explorer__delete.btn.btn-secondary {
  background: #fbe4e4;
  color: #c0392b;
}
.viewers-explorer__delete.btn.btn-secondary:hover {
  background: #c0392b;
  color: #fff;
}
.viewers-build-card__title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 6px;
}
.viewers-build-card__lead {
  font-size: 0.88rem;
  color: var(--text-muted, #6c757d);
  margin-bottom: 16px;
}
.viewers-build-card__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 16px;
}
/* Reframed match score (TS-5123): a coloured full-sentence headline, no number
   or band label. Colour carries the at-a-glance signal. */
.viewers-match-card__heading {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.4;
}
.viewers-match-card__heading--ok { color: #1c7a45; }
.viewers-match-card__heading--warn { color: #9a6a13; }
.viewers-match-card__heading--far { color: #a13333; }
.viewers-match-card__line {
  margin-top: 6px;
  font-size: 0.88rem;
  color: var(--text-color, #1f2937);
  line-height: 1.5;
}

/* Match the seo tools / social composer typography: Satoshi, plus the house
   text tokens (dark-purple --title for headings, --paragraph for muted text)
   instead of the generic slate/bootstrap greys these components shipped with.
   Set as scoped custom properties so every var(--text-color)/var(--text-muted)
   in the rules below resolves to the house palette (TS-5123 / TS-5119). */
.viewers-page,
.competitors-layout {
  --text-color: var(--title, #3e2155);
  --text-muted: var(--paragraph, #6b6880);
  font-family: "Satoshi", sans-serif;
}

/* Viewers v2 fields and dropdowns follow the house CMS form styling (the social
   composer / .control-field look in Image #40): lavender fill, soft radius, a
   muted label and the house focus ring. One scoped rule so every field on the
   Viewers page matches without tagging each one .control-field (TS-5123). */
.viewers-watching .form-control,
.viewers-watching .form-select,
.viewers-explorer .form-control,
.viewers-explorer .form-select {
  font-size: 0.9rem;
  color: #313234;
  border-radius: 8px;
  background-color: var(--primary-light, #f2eef8);
  /* The border was the same colour as the fill, so the field had no boundary:
     1.14 against the surface behind it, where a form control needs 3. This is
     the same defect as the pale buttons, on an input rather than a button, and
     it matters more here because an unbounded field does not read as
     something you can type into. */
  border: 1px solid var(--ts-border-control, #6f6786);
  box-shadow: none;
  padding: 6px 11px;
}
.viewers-watching .form-select,
.viewers-explorer .form-select {
  height: 40px;
  padding-right: 35px;
}
.viewers-watching textarea.form-control,
.viewers-explorer textarea.form-control {
  height: auto;
}
.viewers-watching .form-control::placeholder,
.viewers-explorer .form-control::placeholder {
  color: #9298a5;
  opacity: 1;
}
.viewers-watching .form-control:focus,
.viewers-watching .form-select:focus,
.viewers-explorer .form-control:focus,
.viewers-explorer .form-select:focus {
  border-color: var(--primary-color, #672c72);
  box-shadow: none;
}
.viewers-watching .form-label,
.viewers-explorer .form-label {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-muted, #6c757d);
  margin-bottom: 6px;
}

/* Skeleton shown on the Who's watching tab until get-audiences resolves, so the
   page does not flash the full-width build form before the real state is known
   (TS-5123). Reuses the skeleton-shimmer keyframes, tinted to the house purple. */
.viewers-skeleton__intro {
  text-align: center;
  max-width: 640px;
  margin: 8px auto 16px;
}
.sk-line {
  height: 12px;
  border-radius: 6px;
  background: linear-gradient(90deg, #f0ecf5 25%, #e6ddef 50%, #f0ecf5 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
  margin-bottom: 10px;
}
.sk-line--title {
  height: 16px;
  width: 60%;
  margin: 0 auto 10px;
}
.sk-line--sub {
  height: 10px;
  width: 80%;
  margin: 0 auto;
}
.sk-line--head {
  width: 40%;
  height: 14px;
  margin-bottom: 14px;
}
.sk-line--short {
  width: 55%;
}

/* Current-vs-target comparison view (extension 7.5.1 parity, TS-5123). */
.viewers-compare__intro {
  text-align: center;
  margin: 8px 0 16px;
}
.viewers-compare__title {
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 6px;
}
.viewers-compare__sub {
  font-size: 0.88rem;
  color: var(--text-muted, #6c757d);
  max-width: 640px;
  margin: 0 auto;
}
.viewers-compare__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  margin-bottom: 16px;
}
.viewers-compare__col {
  background: #fff;
  border: 1px solid var(--border-color, #e5e7eb);
  border-radius: 12px;
}
/* The purple edge is the only thing distinguishing "who you want watching"
   from "who's watching now", and at (0,1,0) it was being reset by glass's
   `border` shorthand, so the two columns rendered identically (TS-5507).
   (0,3,0) restores the signal without touching the glass surface itself. */
.viewers-page .viewers-compare__cols .viewers-compare__col--accent {
  border-color: var(--primary-color, #672c72);
}
.viewers-compare__col-title {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 8px;
}
.viewers-compare__col-body {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-color, #1f2937);
  margin: 0;
}
.viewers-compare__col-body--muted {
  color: var(--text-muted, #6c757d);
}
.viewers-compare__focus {
  margin-top: 10px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-muted, #6c757d);
}
.viewers-compare__gap {
  background: #fff;
  border: 1px solid var(--border-color, #e5e7eb);
  border-radius: 12px;
}
.viewers-compare__gap-title {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 6px;
}
.viewers-compare__gap-body {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-color, #1f2937);
}
.viewers-compare__ideas-btn {
  margin-top: 4px;
  /* Content width, left-aligned - not stretched across the card (a card is a
     flex column, so a bare button child would fill the width). */
  align-self: flex-start;
}
.viewers-compare__gap-body--muted {
  color: var(--text-muted, #6c757d);
}
/* On-track reads as a positive, so its card takes the house success edge
   rather than the neutral border. */
.viewers-compare__gap--ok {
  border-color: #1c7a45;
}
.viewers-compare__create {
  margin-top: 14px;
  text-align: center;
}
/* "Review your target" sits under the match line, quiet enough not to compete
   with the comparison below it. */
.viewers-match-card__review {
  margin-top: 12px;
}
/* Single-target name in "Who you want watching". */
.viewers-compare__col-name {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-color, #1f2937);
  margin: 0 0 10px;
}
.viewers-compare__target-actions {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
/* Add-another sits below the Change/Remove row as its own house-secondary
   (orange) button (branding: purple primary, orange secondary). */
.viewers-compare__add {
  /* Content width, centred in the card rather than stretched full-width. */
  align-self: center;
  margin-top: 10px;
}
/* Orange outline treatment for the quieter secondary actions (Remove, Try
   again): the house orange, hollow, filling on hover.
   It used to note that specificity had to beat the solid
   .btn.btn-primary.btn-secondary rule in style.css. That rule is gone, and
   two classes now beat the single-class role definition on their own. */
.btn.btn-secondary.viewers-btn-outline {
  background: #fff;
  border: 1px solid var(--secondary-color, #f26419);
  color: var(--secondary-color, #f26419);
}
.btn.btn-secondary.viewers-btn-outline:hover {
  background: var(--secondary-color, #f26419);
  color: #fff;
}

/* Purple-outline house button, for the quieter target actions (Edit, Rename,
   Remove, Cancel). Specificity beats the solid .btn.btn-primary in style.css. */
.btn.btn-primary.viewers-btn-outline-primary {
  background: #fff;
  border: 1px solid var(--primary-color, #672c72);
  color: var(--primary-color, #672c72);
}
.btn.btn-primary.viewers-btn-outline-primary:hover {
  background: var(--primary-color, #672c72);
  color: #fff;
}

/* Viewers v2 - Viewer explorer (TS-5116, list/profile/edit/compare TS-5123). */
.viewers-explorer__loading {
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
}
.viewers-explorer__intro {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}
.viewers-explorer__intro-text {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
}
.viewers-explorer__group {
  margin-bottom: 20px;
}
.viewers-explorer__group-title {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 8px;
}
.viewers-explorer__group-empty {
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
  margin: 0;
}
/* A viewer row: the whole card opens the profile, the chevron makes that
   explicit (extension 7.5.1). */
.viewers-explorer__row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  margin-bottom: 8px;
  /* Light-purple fill, matching the house form fields (--primary-light). */
  background: var(--primary-light, #f2eef8);
  border: 1px solid var(--primary-light, #f2eef8);
  border-radius: 12px;
}
.viewers-explorer__row--link {
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.viewers-explorer__row--link:hover {
  border-color: var(--primary-color, #672c72);
  background: #fff;
}
.viewers-explorer__row--accent {
  border-color: var(--primary-color, #672c72);
}
.viewers-explorer__row-title {
  flex: 1;
  min-width: 0;
  font-weight: 700;
  color: var(--text-color, #1f2937);
}
.viewers-explorer__row-tags {
  display: flex;
  gap: 6px;
  align-items: center;
}
.viewers-explorer__row-chevron {
  font-size: 1.2rem;
  line-height: 1;
  opacity: 0.5;
  color: var(--text-color, #1f2937);
}
.viewers-explorer__tag {
  font-size: 0.68rem;
  font-weight: 700;
  border: 1px solid var(--border-color, #e5e7eb);
  border-radius: 999px;
  padding: 3px 9px;
}
.viewers-explorer__tag--default {
  border-color: #1c7a45;
  color: #1c7a45 !important;
}
/* Back link to the list. */
.viewers-explorer__back {
  border: 0;
  background: none;
  padding: 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary-color, #672c72);
  cursor: pointer;
  margin-bottom: 12px;
}
.viewers-explorer__profile-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 12px;
}
.viewers-explorer__profile-title {
  flex: 1;
  min-width: 0;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
}
.viewers-explorer__edit-sub {
  font-size: 0.82rem;
  color: var(--text-muted, #6c757d);
  margin-bottom: 12px;
}
.viewers-explorer__profile-row {
  margin-bottom: 12px;
}
.viewers-explorer__profile-label {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 3px;
}
.viewers-explorer__profile-value {
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--text-muted, #55585e);
}
.viewers-explorer__profile-empty {
  font-size: 0.88rem;
  color: var(--text-muted, #6c757d);
}
.viewers-explorer__actions {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
  margin-top: 16px;
}
.viewers-explorer__rename-input {
  flex: 1;
  min-width: 180px;
  max-width: 320px;
}
/* Compare mode. */
.viewers-explorer__cmp-selects,
.viewers-explorer__cmp-names,
.viewers-explorer__cmp-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-bottom: 10px;
}
.viewers-explorer__cmp-name {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
}
.viewers-explorer__cmp-name--accent {
  color: var(--primary-color, #672c72);
}
.viewers-explorer__cmp-cell {
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border-color, #e5e7eb);
  background: #fff;
  font-size: 0.82rem;
  line-height: 1.5;
}
.viewers-explorer__cmp-cell--accent {
  border-color: var(--primary-color, #672c72);
}
/* Per-cell viewer name: hidden on wide screens (the header names row covers it),
   shown when the compare columns stack on narrow screens. */
.viewers-explorer__cmp-cell-name {
  display: none;
}
/* Below the small breakpoint the two viewers are unreadable side by side, so
   stack them: one column for the selects and each field pair, with the inline
   cell name telling the two apart. */
@media (max-width: 575.98px) {
  .viewers-explorer__cmp-selects,
  .viewers-explorer__cmp-cols {
    grid-template-columns: 1fr;
  }
  .viewers-explorer__cmp-names {
    display: none;
  }
  .viewers-explorer__cmp-cell-name {
    display: block;
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--text-color, #1f2937);
    margin-bottom: 4px;
  }
  .viewers-explorer__cmp-cell-name--accent {
    color: var(--primary-color, #672c72);
  }
}
.viewers-explorer__cmp-note {
  font-size: 0.82rem;
  color: var(--text-muted, #6c757d);
}
.viewers-explorer__cmp-loading {
  display: flex;
  justify-content: center;
  padding: 20px 0;
}
.viewers-explorer__cmp-toggle {
  border: 0;
  background: none;
  padding: 0;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--primary-color, #672c72);
  cursor: pointer;
  margin-bottom: 10px;
}
.viewers-explorer__empty {
  padding: 48px 16px;
  text-align: center;
}
.viewers-explorer__empty-title {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text-color, #1f2937);
  margin-bottom: 6px;
}
.viewers-explorer__empty-sub {
  font-size: 0.85rem;
  color: var(--text-muted, #6c757d);
  margin-bottom: 10px;
}
/* The empty-state create action is a house button now, not a text link. */
.viewers-explorer__create-btn {
  margin-top: 4px;
}

/* Filter dropdown */
.cv-filter {
  position: relative;
  outline: none;
}
/* Match the house form fields (Image #40): lavender fill, 8px radius, muted
   text, house focus/hover border - so the filters read as the same control set
   as the Viewers dropdowns rather than white pills. */
.cv-filter__btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  background: var(--primary-light, #f2eef8);
  /* The border was the same colour as the fill, so the control had no edge at
     all: 1.14 against the surface behind it, where a boundary needs 3. Its
     hover already promotes the border to brand purple, which means the resting
     state was the only one without one. */
  border: 1px solid var(--ts-border-control, #6f6786);
  border-radius: 8px 8px 8px 3px;
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  color: var(--title, #313234);
  transition: border-color 0.15s;
}
.cv-filter__btn:hover {
  border-color: var(--primary-color, #672c72);
}
.cv-filter__chevron {
  opacity: 0.5;
  flex-shrink: 0;
}
.cv-filter__dropdown {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  background: #fff;
  border: 1px solid #e7e2f0;
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.12);
  min-width: 140px;
  z-index: 200;
  overflow: hidden;
}
.cv-filter__option {
  display: block;
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: 8px 14px;
  font-size: 0.82rem;
  cursor: pointer;
  color: inherit;
}
.cv-filter__option:hover {
  background: rgba(0,0,0,0.05);
}
.cv-filter__option--active {
  font-weight: 600;
  color: var(--primary-color, #7c3aed);
}

/* Video row */
.cv-video {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 14px;
  border-radius: 12px;
  /* One soft border token shared with the filters, dropdowns and inputs so the
     panel reads as one clean set rather than a mix of weights (TS-5119). */
  border: 1px solid #e7e2f0;
  text-decoration: none !important;
  color: inherit !important;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.cv-video:hover {
  border-color: var(--primary-color, #672c72);
  box-shadow: 0 2px 10px rgba(103, 44, 114, 0.1);
}
.cv-video__thumb {
  width: 120px;
  height: 68px;
  object-fit: cover;
  border-radius: 8px;
  flex-shrink: 0;
  background: #eee;
}
.cv-video__info {
  flex: 1;
  min-width: 0;
}
/* Bold fonts to match the extension (TS-5070 item 3); house title colour. */
.cv-video__title {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--text-color, #3e2155);
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cv-video__channel {
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--text-muted, #6c757d);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Metric stacked as value + label, so the figure is not a bare mystery number
   (matches the extension's "N / views"). */
.cv-video__views {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex-shrink: 0;
  min-width: 56px;
  text-align: right;
}
.cv-video__views-value {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--text-color, #1f2937);
}
.cv-video__views-label {
  font-size: 0.68rem;
  font-weight: 600;
  color: var(--text-muted, #6c757d);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Skeleton for video rows */
.cv-video--skeleton {
  pointer-events: none;
}
.cv-video__thumb--skeleton {
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
}
.cv-video__skeleton-line {
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
  margin-bottom: 6px;
}
.cv-video__skeleton-line--title { width: 80%; }
.cv-video__skeleton-line--channel { width: 45%; }
.cv-video__views--skeleton {
  width: 36px;
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
}

/* Competitor sidebar */
.competitor-sidebar {
  width: 280px;
  flex-shrink: 0;
  /* Flat on the shared white body; a divider separates it from the video list
     instead of its own card. */
  background: transparent;
  border-radius: 0;
  box-shadow: none;
  padding: 0 16px 0 0;
  border-right: 1px solid var(--border-color, #e5e7eb);
}
.competitor-sidebar__heading {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted, #6c757d);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 10px;
  padding-left: 4px;
}
.competitor-sidebar__list {
  list-style: none;
  margin: 0;
  padding: 0;
}
.competitor-sidebar__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 4px;
  border-radius: 8px;
  position: relative;
  cursor: default;
}
.competitor-sidebar__item:hover {
  background: var(--hover-bg, rgba(0,0,0,0.04));
}
.competitor-sidebar__logo {
  object-fit: cover;
  flex-shrink: 0;
}
.competitor-sidebar__info {
  flex: 1;
  min-width: 0;
}
.competitor-sidebar__name {
  font-size: 0.82rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.competitor-sidebar__subs {
  font-size: 0.75rem;
  color: var(--text-muted, #6c757d);
}
.competitor-sidebar__menu-wrap {
  position: relative;
  flex-shrink: 0;
}
.competitor-sidebar__menu-btn {
  background: none;
  border: none;
  padding: 2px 4px;
  cursor: pointer;
  color: var(--text-muted, #6c757d);
  border-radius: 4px;
  line-height: 1;
}
.competitor-sidebar__menu-btn:hover {
  background: var(--hover-bg, rgba(0,0,0,0.07));
}
.competitor-sidebar__dropdown {
  position: absolute;
  right: 0;
  top: 100%;
  background: #fff;
  border: 1px solid rgba(0,0,0,0.1);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  /* Wide enough for the "View channel in YouTube" action (TS-5070 item 4). */
  min-width: 190px;
  z-index: 100;
  overflow: hidden;
  white-space: nowrap;
}
.competitor-sidebar__dropdown-item {
  display: block;
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: 8px 14px;
  font-size: 0.82rem;
  cursor: pointer;
}
.competitor-sidebar__dropdown-item:hover {
  background: rgba(0,0,0,0.05);
}
.competitor-sidebar__dropdown-item--danger {
  color: #dc3545;
}

/* Sidebar track-a-channel form */
.competitor-sidebar__track {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(0,0,0,0.07);
}
.competitor-sidebar__track-label {
  font-size: 0.8rem;
  font-weight: 600;
  margin-bottom: 2px;
}
.competitor-sidebar__track-sub {
  font-size: 0.75rem;
  color: var(--text-muted, #6c757d);
  margin-bottom: 8px;
}
.competitor-sidebar__track-form {
  display: flex;
  gap: 6px;
}
/* Wraps the input so the channel-search suggestions can anchor to it
   (TS-5070 item 1). */
.competitor-sidebar__search {
  position: relative;
  flex: 1;
  min-width: 0;
}
.competitor-sidebar__track-input {
  width: 100%;
  min-width: 0;
  padding: 7px 10px;
  font-size: 0.78rem;
  border: 1px solid #e7e2f0;
  border-radius: 8px;
  outline: none;
  background: var(--input-bg, #f8f9fa);
}
.competitor-sidebar__suggestions {
  position: absolute;
  left: 0;
  right: 0;
  /* Open downward: the sidebar sits high under the admin toolbar, so opening
     upward runs the list off the top of the viewport (TS-5070). */
  top: calc(100% + 4px);
  background: #fff;
  border: 1px solid rgba(0,0,0,0.12);
  border-radius: 8px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.14);
  max-height: 320px;
  overflow-y: auto;
  z-index: 200;
}
.competitor-sidebar__suggestion {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: none;
  border: none;
  text-align: left;
  padding: 6px 10px;
  cursor: pointer;
}
.competitor-sidebar__suggestion:hover {
  background: rgba(0,0,0,0.05);
}
.competitor-sidebar__suggestion-thumb {
  /* !important defeats the global img sizing that was blowing these up on
     dev1 (TS-5119 #1/#2). */
  width: 28px !important;
  height: 28px !important;
  min-width: 28px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.competitor-sidebar__suggestion-label {
  font-size: 0.78rem;
  line-height: 1.3;
  overflow: hidden;
  /* Wrap to two lines so channel names read fully instead of hard-truncating. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}
.competitor-sidebar__track-input:focus {
  border-color: var(--primary-color, #7c3aed);
  background: #fff;
}
.competitor-sidebar__track-btn {
  flex-shrink: 0;
  padding: 5px 10px;
  font-size: 0.78rem;
}

/* Competitor page responsive (TS-5119 #5/#6): stack the sidebar above the video
   panel so videos get full width instead of being squeezed - which is what
   truncated titles to a couple of characters on tablet/mobile. */
@media (max-width: 991px) {
  .competitors-layout {
    flex-direction: column;
    /* Keep the body inside the viewport and clip any horizontal spill from the
       rows (clip, not hidden, so the filter dropdowns still open downward). */
    max-width: 100%;
    overflow-x: clip;
    padding: 14px;
  }
  .competitor-sidebar {
    width: 100%;
    /* Stacked: the divider moves to the bottom edge. */
    border-right: 0;
    border-bottom: 1px solid var(--border-color, #e5e7eb);
    padding: 0 0 16px 0;
    margin-bottom: 4px;
  }
  .cv-panel__list {
    max-height: none;
    overflow-x: hidden;
  }
}
@media (max-width: 575px) {
  .competitors-layout {
    padding: 12px;
  }
  /* Tighter row so the thumbnail + title + metric all fit at ~320px instead of
     pushing the metric off the right edge. */
  .cv-video {
    gap: 10px;
    padding: 10px;
  }
  .cv-video__thumb {
    width: 84px;
    height: 47px;
  }
  .cv-video__views {
    min-width: 40px;
  }
  .cv-video__views-value {
    font-size: 0.9rem;
  }
  .cv-video__views-label {
    font-size: 0.62rem;
  }
}

/* Sidebar skeleton */
.competitor-sidebar__item--skeleton {
  pointer-events: none;
}
.competitor-sidebar__logo-placeholder {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
  flex-shrink: 0;
}
.competitor-sidebar__skeleton-line {
  height: 10px;
  border-radius: 4px;
  background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s infinite;
  margin-bottom: 4px;
}
.competitor-sidebar__skeleton-line--name { width: 100px; }
.competitor-sidebar__skeleton-line--subs { width: 60px; }
@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

img.competitor-video-thumb-img {
  min-width: 125px;
  width: 125px;
  height: 72px;
  object-fit: cover;
}

.edge-case {
  color: var(--primary-color);
}

/* The panel help control, frameless.
 *
 * These declarations were already here and never applied. The element is
 * class "btn btn-light box-icon", and .btn.btn-light in custom-style.css is
 * (0,2,0) against this rule's (0,1,0), so it won: #f8f9fa fill, 1px #6f6786
 * border, on every BlocksWrapper panel in the dashboard.
 *
 * That border rule is not a mistake. It was added so pale low-weight buttons
 * with no perceivable edge (Suggestions, SKIP, Add Outline, Who's watching,
 * Remove all, all between 1 and 1.14 against the surface behind them) cleared
 * WCAG 1.4.11. It caught this control because it shares the class, not
 * because anyone decided a help icon should be a button-shaped box.
 *
 * Frameless is safe under the same rule: 1.4.11 wants a perceivable boundary
 * for a component whose extent you have to judge, and an icon-only control is
 * its own glyph. The glyph carries the contrast instead, #3E2155 at 13.52:1
 * on white and above 10 on the frosted panel, so what has to be seen is seen.
 *
 * Matching .btn.box-icon rather than raising with !important, so it beats the
 * two-class rule on specificity the same way that one beat this. */
.btn.box-icon,
.btn.box-icon:focus,
.btn.box-icon:active {
  padding: 4px;
  margin: 0;
  line-height: unset;
  background: transparent;
  border: 1px solid transparent;
  outline: none;
  box-shadow: none;
  cursor: pointer;
}

/* Removing the frame removes the only hover feedback this control had, on a
   control people already struggle to identify, so the state comes back as a
   tint rather than an edge. Keyboard focus keeps a real ring: an outline the
   frame was previously standing in for. */
.btn.box-icon:hover {
  background: rgba(62, 33, 85, 0.06);
  border-radius: 8px;
}

.btn.box-icon:focus-visible {
  outline: 2px solid var(--primary-color, #672c72);
  outline-offset: 2px;
  border-radius: 8px;
}

.card-block-title {
  font-size: 14px;
  font-weight: bold;
  color: var(--title);
  padding: 0;
  margin-bottom: 8px;
}

.seo-default-msg p {
  margin-top: unset;
  margin-bottom: unset;
}

.doughnut-graph-wrapper {
  width: 100%;
  max-width: 300px;
  height: 100%;
  max-height: 300px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.doughnut-graph-color-dot {
  width: 12px;
  height: 12px;
}

.doughnut-graph-block {
  height: 100%;
  width: 100%;
}

.chart-graph-wrapper {
  width: 100%;
  height: 250px;
  position: relative;
}

.lexical-word-label {
  border: none;
  padding: 5px 18px;
  border-radius: 8px;
  font-weight: bold;
}

.progress-bar-bg {
  background-color: #e9ecef;
  height: 10px;
}

#add-social-post .post-preview .wrapper-preview {
  height: inherit;
  position: relative;
}

.social-post-scroll-hidden{
  max-height: 195px;
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.social-post-scroll-hidden::-webkit-scrollbar {
  display: none;
}
/*  21 April 2025 */

/* The pale fill measured 1.14:1 against the card behind it, so the button had
   no perceivable edge: the label was legible but the control's boundary was
   not, which is the 3:1 non-text threshold rather than the 4.5 text one.
   Same failure as btn-light had, and the same fix. A border rather than a
   heavier fill, because these repeat many times per page and should stay
   quiet. var(--ts-border-control, #6f6786) is 5.30:1 on a white card and 4.27:1 on a frosted panel. */
.suggestionsBtn {
  padding: 6px 15px;
  box-shadow: none !important;
  display: inline-block;
  text-align: center;
  border-radius: 8px;
  border: 1px solid var(--ts-border-control, #6f6786);
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  background-color: var(--primary-light);
  color: var(--primary-color);
}

.suggestionsBtn:hover {
  background-color: var(--primary-color);
  color: #ffffff;
}

/* Primary variant: solid purple with white text. Use when the button sits
 * on a coloured card (e.g. the recommendation panel inside Suggestions),
 * where the default light-on-light suggestionsBtn becomes invisible. */
.suggestionsBtn--primary {
  background-color: var(--primary-color);
  color: #ffffff;
}

.suggestionsBtn--primary:hover {
  background-color: #5a2561;
  color: #ffffff;
}

.seo-tools-tabs.nav-tabs .nav-link:focus,
.seo-tools-tabs.nav-tabs .nav-link:hover {
  border-color: #ffffff;
}

.seo-tools-title-form .control-field.input-group {
  background: var(--primary-light);
  border-radius: 6px;
  padding: 6px;
}

.primary-color {
  color: var(--primary-color);
}

.seo-tools-title-form .control-field input.form-control::placeholder {
  color: #3e2155;
  font-size: 12px;
  font-weight: 700;
  line-height: 16px;
}

.progressiconic-wrapper {
  flex-wrap: wrap;
  max-width: 300px;
}

.progressiconic-wrapper > svg {
  width: 100%;
  flex: 1;
  height: 100%;
}

.seo-tools-tabs .nav-link.active {
  position: relative;
  z-index: 2;
}

/* Corner-notch pseudo-elements disabled. They were designed for the
 * old #f2eef8 active-tab colour and do not render correctly now the
 * active tab shares the white content-area background; the rounded
 * top corners on the active tab already provide sufficient shape. */

/*  21 April 2025 */
/*  create project from homerun start */
.custom-list-item {
  background-color: #f2eef8;
  padding: 0px;
  border: unset;
}
.video-project-thumbnail {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
/* bootstrap pager */
.bt-pager .page-item span,
.bt-pager .page-item a {
  border: 0;
}
.bt-pager .page-item.active span.page-link {
  background-color: var(--primary-color);
  border-radius: 6px;
  width: 32px;
  height: 32px;
}

/* video tree Css start */
.ts-video-tree .zoom-control {
  width: 100px;
  align-items: center;
  height: 35px;
  /* Was a bare var(--ts-lightest-primary-color). That name is defined by the
     BROWSER EXTENSION, which appends a :root block to document.head on every
     page it runs on, this site included. The CMS defines it nowhere. So the
     zoom control had a background only for users who happened to have the
     extension installed, and rendered transparent for everyone else: an
     unresolved var() computes to nothing, not to a default.
     --primary-light is the house page tint and is what this always meant. */
  background: var(--primary-light, #F2EEF8);
  gap: 10px;
  border-radius: 25px;
  user-select: none; /* Standard */
  -webkit-user-select: none; /* Safari */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE/Edge */
}
.ts-video-tree .video-control-wrapper {
  padding: 12px;
}
.ts-video-tree .video-card-container {
  position: absolute;
  top: 20px;
  left: 30px;
  z-index: 9999;
}
.ts-video-tree .relation-card-container {
  position: absolute;
  top: 20px;
  right: 30px;
  z-index: 9999;
}
.ts-video-tree .video-card {
  width: 22rem;
  background-color: #f5f8ff;
  border-radius: 10px;
  padding: 10px 4px;
  -webkit-user-select: none; /* Chrome, Safari */
  -moz-user-select: none; /* Firefox */
  -ms-user-select: none; /* IE 10+ */
  user-select: none; /* Standard */
}

.ts-video-tree .video-card-title {
  color: #6c757d;
  font-size: 1rem;
  margin: 0;
}
.ts-video-tree .relation-item {
  cursor: pointer;
  overflow: auto;
}

.ts-video-tree .cursor-move {
  cursor: move;
}

.ts-video-tree .thumbnail-card {
  cursor: pointer;
  padding: 2px;
  overflow: hidden;
}

.ts-video-tree .thumbnail-img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}
.ts-video-tree .thumbnail-disabled {
  opacity: 0.4;
  pointer-events: none;
}
/* Badge shown on thumbnails that are already added to the video tree */
.ts-video-tree .already-added-badge {
  position: absolute;
  right: 6px;
  top: 6px;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  font-size: 11px;
  padding: 3px 6px;
  border-radius: 12px;
  z-index: 10;
}

.ts-video-tree .thumbnail-already-added {
  position: relative;
}
.ts-video-tree .VideoCanvasContainer {
  position: relative;
  width: 100%;
  height: 88%;
}
.ts-video-tree .context-menu {
  position: absolute;
  background-color: white;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px;
  z-index: 1000;
}

.ts-video-tree .context-menu ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.ts-video-tree .context-menu li {
  padding: 8px 12px;
  cursor: pointer;
}

.ts-video-tree .context-menu li:hover {
  background-color: #f0f0f0;
}

/* video tree css end */

.homerun-profile-grid-wrapper .checkmark {
  width: 26px;
  height: 26px;
  top: 2px;
  right: 4px;
}
.aspect-16-9 {
  aspect-ratio: 16/9;
}
.aspect-16-9 img {
  object-fit: contain;
  width: 100%;
  height: 100%;
}
.project-listtable-custom-main .rdt_TableRow {
  flex-wrap: wrap;
}
.project-delete-modal .node-confirm-form .form-actions .dialog-cancel {
  display: none !important;
}
.channel-banner-wrapper {
  width: 100%;
  height: 288px;
  overflow: hidden;
  border-radius: 8px;
  background-color: var(--primary-light);
  margin-top: 10px;
}
.yes-no-progress-iconic {
  width: 45px;
}
.text-danger a {
  color: #dc3545;
}

.image-tabs {
  display: flex;
  border-bottom: 2px solid #e5e7eb;
}

.image-tab {
  padding: 6px 10px;
  font-weight: 500;
  cursor: pointer;
  background: transparent;
  border: none;
  border-bottom: 3px solid transparent;
  color: #f26419;
  transition: all 0.2s ease;
  border-radius: 12px 12px 0px 0px;
}

.image-tab:hover:not(.disabled) {
  opacity: 0.85;
}

.image-tab.active {
  background: var(--primary-color);
  color: white;
}

/* A disabled tab must not look livelier than the enabled one.
 *
 * This restated the same brand orange as .image-tab and added only
 * cursor: not-allowed, which is invisible until you are already hovering. The
 * result was a disabled tab rendered in full brand orange at full opacity,
 * sitting beside the active tab and reading as the most clickable thing in
 * the group. Disabled controls are exempt from the contrast thresholds, but
 * they are not exempt from telling the truth about whether you can press
 * them. */
.image-tab.disabled {
  color: var(--ts-border-control, #6f6786);
  opacity: 0.6;
  cursor: not-allowed;
}

.preview-mode-switch.form-switch input.form-check-input {
  background-color: #fff;
}
.date-title {
  font-size: 14px;
  font-weight: bold;
  color: var(--title);
}
.event-post-wrapper {
  background-color: var(--primary-light);
}
.event-post {
  max-width: 80%;
  min-width: 0;
  font-size: 14px;
  color: var(--title);
}

/*  create project from homerun end */

@media (max-width: 1650px) {
  #add-social-post .post-preview .wrapper-preview {
    min-height: 140px;
  }
}

@media screen and (max-width: 1306px) {
  #add-social-post .post-preview .wrapper-preview {
    min-height: 105px;
  }
}

@media (max-width: 1199px) {
  .rmdp-day-picker > div {
    width: 50%;
  }

  .rmdp-wrapper .rmdp-top-class,
  .rmdp-wrapper .rmdp-calendar {
    width: 100% !important;
  }

  .rmdp-day,
  .rmdp-week-day {
    width: 48px !important;
    height: 48px !important;
  }

  .rmdp-wrapper {
    margin-top: 16px;
    width: 100% !important;
  }
}

@media screen and (max-width: 992px) {
  .path-channel-report #block-reactdonextblock-2 .report-boxes {
    width: 100%;
  }

  .blankLabel {
    display: none;
  }

  .path-channel-report #block-reactdonextblock-2 {
    flex: 0 48%;
    margin: 0px;
    margin-right: 10px;
  }

  .social-post-text-preview .action-btn-group {
    gap: 16px;
  }

  .rmdp-day,
  .rmdp-week-day {
    width: 32px !important;
    height: 32px !important;
  }

  .calendarWrapper {
    overflow: auto;
  }

  .calendarWrapper .rmdp-calendar {
    min-width: 500px;
  }

  #add-social-post .post-preview .wrapper-preview {
    min-height: 96px;
    position: relative;
  }
  .homerun-profile-grid-wrapper .checkmark {
    width: 18px;
    height: 18px;
    top: 1px;
    right: 1px;
  }
}

@media screen and (max-width: 767px) {
  .path-channel-report #block-reactdonextblock-2 .report-boxes {
    width: 100%;
  }

  .path-channel-report #block-reactdonextblock-2 {
    margin: 0;
    width: 100%;
    flex: unset;
  }

  .button_autoBox {
    gap: 16px;
  }
}

@media (max-width: 575px) {
  .highlightBox {
    flex-direction: column;
    gap: 8px;
    margin: 10px 0;
  }
}
@media (max-width: 380px) {
  .progressiconic-wrapper {
    width: 100%;
  }
  .progressiconic-wrapper.yes-no-progress-iconic {
    width: 45px;
  }
  .channel-audit .btnWrapper {
    margin-top: 15px;
  }
}
.VideoCanvasContainer .canvas.lower-canvas {
  width: 100% !important;
}
/* Channel Audit layout fixes */
.channel-audit .seo-sidebar-wrapper .mb-3:last-child,
.channel-audit .seo-youtube-search-preview-wrapper .mb-3:last-child {
  margin-bottom: 0.5rem !important;
}
/* One gap between cards, not three.
 *
 * The page ran 16px under most cards, 12px under the two carrying
 * .keyword-card and 8px under the checklist, because these two rules
 * overrode the mb-3 already in the markup. Which gap a card got depended on
 * which wrapper class it happened to have rather than on anything the reader
 * can see, so the column read as slightly broken rhythm all the way down.
 *
 * Both now match the mb-3 the rest of the page uses. Kept as explicit rules
 * rather than deleted, because the !important they carried means simply
 * removing them would leave the markup's mb-3 to win, which is the same
 * result but leaves nothing to explain why the tighter values are gone. */
.channel-audit .seo-suggestions-wrapper,
.channel-audit .keyword-card {
  margin-bottom: 1rem !important;
}
/* The last block in a card does not add its own trailing margin.
 *
 * Every score card ends 17px above the card edge, which is the card's own
 * padding. Channel Momentum ended 25px above it, because it is the one card
 * with no Suggestions button, so the last thing in it is the content block and
 * its mb-2 is left dangling on top of the padding.
 *
 * Scanning the column, that card reads as having a gap where the others have a
 * button. The padding is what should set the distance to the edge, not
 * whatever the last child happened to bring with it. */
.channel-audit .card > .middle-content-block:last-child {
  /* !important because the block carries Bootstrap's .mb-2, and the spacing
     utilities are !important like the display and radius ones. Fourth time
     today a well-scoped theme rule has lost silently to a utility class. */
  margin-bottom: 0 !important;
}

/* Channel-audit inline scores sizing */
.channel-audit-inline-scores .progressiconic-wrapper {
  max-width: 200px !important;
  width: 100%;
}
/* Meter and score pill on one line, wrapping to two when the card is narrow.
 * Applied to both Channel Audit and Discovery Optimiser/SEO Optimiser.
 *
 * This was two equal columns with both children centred in their own half.
 * That centring targeted .progressiconic-wrapper, which is the meter this
 * component USED to render; it draws a shared GradeMeter now, so the rule
 * stopped matching and the meter fell to the left edge of its column with a
 * two hundred pixel hole between it and the pill.
 *
 * Flex rather than a repaired grid, because the real problem is that the pair
 * does not always fit on one line. A grid cannot wrap, so on a narrow card the
 * pill was pushed hard against the meter with no space at all. A row-gap is
 * what fixes that, and it only exists once the row can wrap. */
.channel-audit .seo-block-score-row,
.seo-tools-wrapper .seo-block-score-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 10px;
}
.channel-audit .seo-block-score-row .progressiconic-wrapper,
.seo-tools-wrapper .seo-block-score-row .progressiconic-wrapper {
  max-width: 200px !important;
  width: 200px;
}
.channel-audit .seo-block-score-row .ts-grade-meter,
.seo-tools-wrapper .seo-block-score-row .ts-grade-meter {
  flex: 0 1 auto;
}
/* 12px in #858B98 measures 3.42:1 on the white card, well under the 4.5 this
   size needs. It is the sentence explaining the score, so it is the part of
   the card worth reading, and it was the least legible thing on the page. */
/* Scoped to the page, not to a wrapper class. Keying on .keyword-tab-block
   missed Device Optimization, which sits in .keyword-card, so one card out of
   eight kept the 12px and the 3.42:1 while its neighbours were fixed. The
   wrapper a card happens to be in is not what decides how its body copy should
   read. */
.channel-audit .middle-content-block .small,
.channel-audit .card-text.small {
  font-size: 13px;
  line-height: 1.5;
  color: #5b5872;
}
.channel-audit .keyword-tab-block .edge-case {
  color: #e65100;
  font-size: 12px;
}
.channel-audit .keyword-tab-block .btn-mark {
  font-size: 13px;
  padding: 6px 0;
}

/* Banner feedback panel (right column) */
.channel-audit-banner-feedback {
  background: #f9f7fa;
  border-radius: 8px;
  padding: 16px;
  height: 100%;
}
.channel-audit-feedback-text {
  font-size: 13px;
  line-height: 1.7;
  color: #555;
  margin-bottom: 0;
}
/* Banner image fills its column */
.channel-banner-wrapper-full {
  width: 100%;
  overflow: hidden;
  border-radius: 8px;
  background-color: var(--primary-light);
}
.channel-banner-wrapper-full .banner-img {
  width: 100%;
  height: auto;
  display: block;
}

/* Inline scores row (banner + description sections) */
.channel-audit-inline-scores {
  display: flex;
  flex-wrap: wrap;
  /* Was 0. Harmless when each column held a narrow ring, but the grade meters
     fill their column, so with no gutter five of them met edge to edge and
     read as one meter spanning the card rather than five separate scores. */
  gap: 14px 20px;
  padding: 12px 0 4px;
  border-top: 1px solid #eee;
  margin-top: 8px;
}
/* These columns centre their label, grade and text, so the meter centres with
   them. Left-aligning it inside a centred column is what made the row look
   ragged. */
.channel-audit-inline-score-item .ts-grade-meter {
  align-self: center;
}
/* The Yes/No columns have no meter, so their badge sat where the other
   columns put their meter and every label below it was 20px out of step.
   The spacer holds the meter's slot open so the row reads as a row. */
.channel-audit-inline-score-spacer {
  display: block;
  height: 20px;
}

/* The grade sat right up against the meter above it. The meter is a graphic
   and the grade is the read, so they need to separate or the eye takes them
   as one block. */
.channel-audit-inline-score-item .ts-grade-meter {
  margin-bottom: 8px;
}

/* Summary panel: the LETTER is what lines up, not the label.
 *
 * The row is space-between, so the score tag sat flush right and the letter's
 * position moved with the length of the word beside it: "Fine" and "Needs
 * Improvement" put the C and the D in different places, and the column read as
 * broken. The letter is the anchor a reader scans down, so it gets a fixed
 * slot and the word runs on from it. Wide enough for "Needs Improvement" on
 * two lines, which is the longest label in the set. */
.channel-audit-summary-score {
  flex: 0 0 132px;
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
/* The letter and the word BOTH carry .keyword-badge, which is the trap here:
 * styling the class hits the word too. Giving the word flex: 0 0 auto stopped
 * it shrinking, so "Needs Improvement" demanded 155px inside a 132px slot and
 * ran out past the card edge instead of wrapping onto a second line.
 *
 * They are told apart by element: ScoreTag renders the letter as a span and
 * HighlightTags renders the word as a div. The letter holds a fixed slot so
 * letters line up down the column; the word takes what is left and wraps. */
.channel-audit-summary-score > span {
  width: 100%;
}
.channel-audit-summary-score span.keyword-badge {
  min-width: 30px;
  text-align: center;
  flex: 0 0 auto;
}
.channel-audit-summary-score div.keyword-badge {
  flex: 1 1 auto;
  min-width: 0;
  white-space: normal;
}
.channel-audit-inline-score-item {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
}
.channel-audit-score-label {
  font-size: 11px;
  font-weight: 600;
  color: #555;
  white-space: nowrap;
}
.channel-audit-inline-score-item {
  display: flex;
  flex-direction: column;
  /* Centred, every one of the five columns had two ragged edges and nothing
     for the eye to run down. Left aligned they share a single edge. */
  align-items: flex-start;
  text-align: left;
}
.channel-audit-inline-score-item .progressiconic-wrapper,
.channel-audit-inline-scores .progressiconic-wrapper {
  margin-bottom: 2px;
}
.channel-audit-inline-score-item .yes-no-progress-iconic,
.channel-audit-inline-scores .yes-no-progress-iconic {
  transform: scale(0.75);
  transform-origin: center top;
  margin-bottom: -2px;
}
.channel-audit-yesno-badge {
  display: inline-block;
  padding: 3px 14px;
  border-radius: 12px;
  font-size: 12px;
  font-weight: 700;
  margin: 4px 0;
}
.channel-audit-yesno-badge.badge-yes {
  background: #e8f5e9;
  color: #2e7d32;
}
.channel-audit-yesno-badge.badge-no {
  background: #fbe9e7;
  color: #c62828;
}
/* Was 11px #777 centred in a 160px column, which is three or four words a
   line and a ragged shape on both edges. #777 also measures 4.48:1 on white,
   under the 4.5 needed for text this size, so it failed the threshold as well
   as being hard to read.

   Left aligned with one straight edge, at 13px, in an ink that holds 6.81:1.
   The max-width goes too: the column is already the constraint, and capping it
   at 160 inside a 215px column was what forced the extra wrapping. */
.channel-audit-score-context {
  font-size: 13px;
  line-height: 1.45;
  color: #5b5872;
  margin: 4px 0 0;
  text-align: left;
  width: 100%;
}
@media (max-width: 991px) {
  .channel-audit-inline-scores {
    flex-wrap: wrap;
    gap: 8px;
  }
  .channel-audit-inline-score-item {
    flex: 1 1 30%;
  }
}
/* The 30% basis above is a TABLET rule, and until now it was the only media
   query on this page, so it governed phones too: 3-up held from 991px all the
   way down to 320px. On a 360px phone three description paragraphs shared the
   card width, each column came out near 100px, and the text broke to one or
   two words a line (TS-5508).
   flex-wrap could not rescue it. With a 30% basis and min-width: 0 the items
   shrink rather than wrap, so the row never breaks on its own.
   Two steps down, on Bootstrap's own breakpoints. Going straight to 1-up at
   767px would leave a ~730px column holding a short paragraph, which reads
   sparse, so md-to-sm gets pairs first. */
@media (max-width: 767px) {
  .channel-audit-inline-score-item {
    flex: 1 1 45%;
  }
}
/* Phones: one score per row. This also clears two downstream symptoms of the
   same squeeze, which is why neither needs its own fix - the 200px grade meter
   stops overflowing its column, and .channel-audit-score-label's
   white-space: nowrap has room to sit on one line. */
@media (max-width: 575px) {
  .channel-audit-inline-score-item {
    flex: 1 1 100%;
  }
}
/* SEO react page end*/

.target-flex-1 {
  flex: 1;
}

.btnWrapper:has(.btn-analyze-view) {
  display: flex;
  align-items: end;
}
/* Prevent Load/Analyze buttons from being pushed off-screen. */
.target-view .btn-analyze-view {
  flex-shrink: 0;
  white-space: nowrap;
}
.target-view {
  gap: 8px !important;
}

/* Tighten the Bootstrap column gutter between the Title field and the
 * target-view (Target Viewer + Load + Analyze) so the gap between them
 * matches the 8px gap between items inside target-view. Without this
 * the default ~24px grid gutter made the Title-to-Target-Viewer gap
 * visibly larger than the Target-Viewer-to-Load gap. */
.seo-tools-title-form > .row > .row > .col,
.seo-tools-title-form > .row > .row > [class^="col-"],
.seo-tools-title-form > .row > .row > [class*=" col-"] {
  padding-left: 4px;
  padding-right: 4px;
}
.seo-tools-title-form > .row > .row {
  margin-left: -4px;
  margin-right: -4px;
}

@media screen and (max-width: 991px) {
  .target-view {
    flex-direction: column;
    align-items: start !important;
  }
}

/* At 992–1199 px the right column (5/12) is too narrow (~310 px) to
   fit Target Viewer select + Load + Analyze side-by-side without the
   select collapsing to ~140 px and its dropdown overflowing.
   Wrap the select to its own line so it gets full column width. */
@media screen and (min-width: 992px) and (max-width: 1199px) {
  .target-view {
    flex-wrap: wrap;
    align-items: flex-end;
  }
  .target-view .target-flex-1 {
    flex: 0 0 100%;
    max-width: 100%;
  }
}

/* ---- Emotion Image Manager ---- */

.emotion-slot-card {
  border: 1px solid #e8e0f0;
  border-radius: 8px;
  overflow: hidden;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.emotion-slot-card:hover {
  border-color: var(--primary-color);
  box-shadow: 0 2px 8px rgba(103, 44, 114, 0.1);
}
.emotion-slot-card--dragover {
  border: 2px dashed var(--primary-color);
  background: #faf8fd;
}

.emotion-slot-preview {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #f5f0f8;
}
.emotion-slot-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.emotion-slot-emoji {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3.5rem;
  background: linear-gradient(135deg, #f8f5fc 0%, #ece4f3 100%);
}

.emotion-slot-drop-overlay {
  position: absolute;
  inset: 0;
  background: rgba(103, 44, 114, 0.15);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--primary-color);
  gap: 4px;
  font-size: 0.85rem;
  font-weight: 600;
}

.emotion-slot-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: #3E2155;
  line-height: 1.3;
  flex: 1;
  min-width: 0;
}

.emotion-slot-badge {
  font-size: 0.7rem;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 10px;
  flex-shrink: 0;
}
.emotion-slot-badge--custom {
  background: #eaf7ef;
  color: #2BC485;
}
.emotion-slot-badge--default {
  background: #f0f0f0;
  color: #888;
}

.emotion-slot-btn--upload {
  background: var(--primary-color);
  border-color: var(--primary-color);
  color: #fff;
  font-size: 0.8rem;
}
.emotion-slot-btn--upload:hover {
  background: #5a2561;
  border-color: #5a2561;
}
.emotion-slot-btn {
  font-size: 0.8rem;
}

/* The Discovery Optimiser score block layout used to be repeated here, a
 * near-copy of the rule above for the same two selectors. Being later in the
 * file it quietly won, so editing the first block appeared to do nothing.
 * One definition now, further up, covering both surfaces. */

/* Equalise score-block heights and pin the action button to the bottom.
 * The Discovery Optimiser and SEO Optimiser score blocks share a row,
 * but each has different amounts of content (Title Length has the big
 * 9 / 57 stats, Readability is just text). Without flex column on the
 * card, the row is the height of the tallest block and the shorter
 * blocks leave dead space below their button. With flex column +
 * middle-content-block flex-grow, the middle content stretches and
 * the button always sits at the bottom of the card. */
.title-blocks-wrapper > .card,
.keyword-tab-block > .card {
  display: flex;
  flex-direction: column;
}
.title-blocks-wrapper .middle-content-block,
.keyword-tab-block .middle-content-block {
  flex-grow: 1;
}

/* Suggestions / Keyword Workshop button on score blocks: centred with
 * a constrained width so it reads as a button. Without max-width, the
 * button stretched to the block width and looked like a footer band. */
.title-blocks-wrapper .btn-mark,
.keyword-tab-block .btn-mark {
  align-self: center;
  min-width: 140px;
  max-width: 240px;
  margin-top: 12px;
}

/* Disclaimer block: visually subordinate to the score blocks above.
 * Smaller text, muted colour, lighter card framing. Reads as a footer
 * note, not a peer of the actionable analysis blocks. */
.disclaimer-block > .card {
  background: transparent !important;
  box-shadow: none !important;
  border: 1px solid #EAE2F4;
  padding: 12px !important;
}
.disclaimer-block .card-title {
  font-size: 12px;
  color: var(--paragraph, #6b6880);
  font-weight: 600;
  margin-bottom: 6px;
}
.disclaimer-block p {
  font-size: 12px;
  color: var(--paragraph, #6b6880);
  line-height: 1.5;
  margin-bottom: 0;
}

/* B's panel frame. Wraps the whole middle-column block so the pills
 * and descriptors read as one cohesive panel instead of text floating
 * in whitespace. Tint is a very soft lavender so the panel sits
 * quietly inside the outer white card without competing visually
 * with the coloured pills inside it. A 1px border gives a clean
 * edge; the rounded corners match the radius on the coloured pills
 * and on the score-block bars to the right. Padding is generous so
 * the content does not feel cramped against the frame. */
.seo-sidebar-panel {
  background-color: #FAF6FC;
  border: 1px solid #E6DCEB;
  border-radius: 10px;
  padding: 16px 20px;
}
.seo-sidebar-panel > *:last-child {
  margin-bottom: 0 !important;
}

/* SEO Sidebar middle column (Column B, 35% wide).
 *
 * Each row pairs a descriptor with its pill. Earlier iterations tried
 * two extremes: fixed 170px min-width (which pushed pills past the
 * column edge) and space-between (which stranded the descriptor and
 * pill at opposite sides of the column with a visible void between
 * them). Neither read cleanly.
 *
 * The current pattern is flex-start with a fixed min-width on the
 * descriptor -- ~150px -- paired with a shared align-items:center so
 * the pair reads as one tidy group. The pills across rows align to
 * the same x position and the free space is all to the right of the
 * group, which feels intentional rather than jagged. */
.seo-sidebar-middle .d-flex,
.seo-sidebar-middle .card-info {
  align-items: center;
  gap: 12px;
}
.seo-sidebar-middle .card-block-title,
.seo-sidebar-middle .form-label.card-block-title {
  min-width: 180px;
  margin-bottom: 0;
}
/* Learning Skills / Search Stats sub-rows: align the left descriptor
 * so value pills across rows share the same x position. Matches the
 * 180px min-width above so the sub-row pill column aligns visually
 * with the top-level pill column. */
.seo-sidebar-middle .card-info > :first-child {
  min-width: 180px;
}
/* Plain-text descriptor for the Search Stats rows. Deliberately NOT
 * styled as a pill: the reader should read "Monthly Searches" as a
 * category label, not mistake it for a data point. Muted grey keeps
 * it quieter than the coloured value pill on the right. */
.seo-sidebar-middle .seo-stat-label {
  font-size: 13px;
  color: #5f6570;
  font-weight: 500;
}

/* Right-hand score-block columns (C1 and C2, 20% wide each).
 *
 * The label sits on the left and the score tag sits on the right of
 * the same row, with the bar below. On Discovery Optimiser this
 * column shares the same class but is wider, so a 170px label
 * min-width used to work there; on Search Explorer where C1/C2 are
 * only 20% of the row, 170px forced the score tag to wrap onto a
 * second line ("Top Tier", "Very Good" broke in half).
 *
 * Drop the min-width and fall back to the Bootstrap default
 * justify-content:space-between so the label hugs the left edge
 * and the score tag hugs the right. The label shrinks naturally to
 * its content width and the pill always fits on one line. align-items
 * keeps them on the same baseline. */
.seo-sidebar-right .d-flex.justify-content-between {
  align-items: center;
  gap: 8px;
}
.seo-sidebar-right .card-block-title,
.seo-sidebar-right .form-label.card-block-title {
  margin-bottom: 0;
}
/* Allow the score tag on the right to keep its words on one line;
 * wrapping a short phrase like "Top Tier" into two lines reads as
 * a layout bug. The label next to it can shrink to fit. */
.seo-sidebar-right .keyword-badge.custom-labels {
  white-space: nowrap;
}
/* Let the right-column score bars fill the available column width.
 * The previous 260px cap was set when the column was narrower; on
 * the rebalanced layout it left a visible void between the bars
 * and the right edge of the section. With the muted monochrome
 * fill the bars no longer feel "loud" enough to dominate when
 * stretched to full width. */
.seo-sidebar-right .progressiconic-wrapper {
  max-width: 100%;
}

/* Search Explorer sidebar: stack all four columns to full-width on
 * screens below lg (991px). The columns use inline flex-basis
 * percentages for the desktop 4-column layout, so !important is
 * required to override them at smaller breakpoints. */
@media (max-width: 991px) {
  .seo-sidebar-wrapper .row > [class*="col"] {
    flex: 0 0 100% !important;
    max-width: 100% !important;
  }
}

/* At 992–1399 px the 20% column A is too narrow for the CircularProgressBar
 * which has a hardcoded inline width/height of 200 px. Override both the
 * wrapper div[style] and its canvas so the donut scales to the column. */
@media (min-width: 992px) and (max-width: 1399px) {
  .seo-sidebar-wrapper .row > .col-md-12:first-child > div[style] {
    width: 100% !important;
    max-width: 200px !important;
    height: auto !important;
    aspect-ratio: 1;
  }
  .seo-sidebar-wrapper .row > .col-md-12:first-child canvas {
    width: 100% !important;
    height: 100% !important;
  }
}

/* The two blocks below re-proportion the FOUR-COLUMN sidebar (SeoSidebar's
 * A + B + C1 + C2). They are constrained to rows that genuinely have four
 * children via the :nth-last-child() count guard, and that guard is load
 * bearing - do not simplify it away.
 *
 * Without it these rules matched ANY row inside .seo-sidebar-wrapper whose
 * first child carried col-md-12, because the selector never mentions the xl
 * classes that set the real proportions. Two other pages put a TWO-column
 * row in that wrapper - the Channel Audit's TopSection and
 * VideoReportTopSection, both col-xl-9 + col-xl-3 - so between 1200 and
 * 1399 px their 75/25 split was overridden to 25% and 32%, collapsing the
 * score column to about 60 px and wrapping its heading one letter per line.
 * Between 992 and 1199 px they got 33/67, which is merely wrong rather than
 * broken, which is why it went unnoticed.
 *
 * It only bit between 1200 and 1399 px, so it was invisible on a laptop
 * below that and on a wide monitor above it, while covering common 1280 and
 * 1366 widths.
 */

/* 992–1199 px: switch from a 4-col single-row to a 2-row layout.
 * Row 1 → A (33%) + B (67%)   Row 2 → C1 (50%) + C2 (50%)
 * Gives column A ~200 px for the donut, C1/C2 ~300 px for the bars. */
@media (min-width: 992px) and (max-width: 1199px) {
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(1):nth-last-child(4) {
    flex: 0 0 33% !important;
    max-width: 33% !important;
  }
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(2):nth-last-child(3) {
    flex: 0 0 67% !important;
    max-width: 67% !important;
  }
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(3):nth-last-child(2),
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(4):nth-last-child(1) {
    flex: 0 0 50% !important;
    max-width: 50% !important;
  }
}

/* 1200–1399 px: keep 4 columns but widen A from 20% → 25% so the donut
 * fits without clipping; redistribute the difference across B, C1, C2. */
@media (min-width: 1200px) and (max-width: 1399px) {
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(1):nth-last-child(4) {
    flex: 0 0 25% !important;
    max-width: 25% !important;
  }
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(2):nth-last-child(3) {
    flex: 0 0 32% !important;
    max-width: 32% !important;
  }
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(3):nth-last-child(2),
  .seo-sidebar-wrapper .row > .col-md-12:nth-child(4):nth-last-child(1) {
    flex: 0 0 21.5% !important;
    max-width: 21.5% !important;
  }
}

/* ── SidebarComponent (Discovery Optimiser / Title Workshop) ──────────
 * Uses Bootstrap col-lg-9/3 outer + col-lg-4/8 inner. At 992–1199 px
 * (≈ 700 px card) the columns are too narrow:
 *   col-lg-4 donut  → ~100 px content (200 px chart overflows badly)
 *   col-lg-3 scores → ~145 px content (5×30 px SVGs + labels wrap)
 * Rebalance columns and scale the 200 px-hardcoded CircularProgressBar. */

/* Scale the donut at all intermediate breakpoints */
@media (min-width: 992px) and (max-width: 1399px) {
  .title-workshop-sidebar-wrapper .col-lg-4 > div[style] {
    width: 100% !important;
    max-width: 200px !important;
    height: auto !important;
    aspect-ratio: 1;
  }
  .title-workshop-sidebar-wrapper .col-lg-4 canvas {
    width: 100% !important;
    height: 100% !important;
  }
}

/* 992–1199 px: widen right score column and give donut half of left */
@media (min-width: 992px) and (max-width: 1199px) {
  .title-workshop-sidebar-wrapper .col-lg-9 {
    flex: 0 0 60%;
    max-width: 60%;
  }
  .title-workshop-sidebar-wrapper .col-lg-3 {
    flex: 0 0 40%;
    max-width: 40%;
  }
  .title-workshop-sidebar-wrapper .col-lg-9 .col-lg-4 {
    flex: 0 0 50%;
    max-width: 50%;
  }
  .title-workshop-sidebar-wrapper .col-lg-9 .col-lg-8 {
    flex: 0 0 50%;
    max-width: 50%;
  }
}

/* Brand-monochrome score bar (Direction 1 mock). Replaces the five
 * red/orange/yellow/green frames and the smiley faces with a clean
 * gradient of brand-purple shades. Implemented as CSS overrides on
 * the existing ProgressFrameA-F SVGs so we can revert in seconds
 * if the new look does not stick. If we keep it, fold the muted
 * values back into the SVG sources and drop the runtime overrides.
 *
 * The :nth-of-type(N) selector picks out the five frame SVGs in
 * order (B, C, D, E, F) regardless of the position-absolute marker
 * div sitting before them. The ` > svg` constraint avoids hitting
 * the new Sparkle SVG which is not a direct svg child. */
/* Fill-to-score bar. Frames default to an "empty" light lavender;
 * the wrapper's data-reached attribute (0-5) tells CSS how many of
 * the five frames to fill with brand purple. Each frame is a
 * 20-point band, so the fill is granular to the nearest 20% of
 * the score. The exact numeric score is still shown by the
 * ScoreTag pill next to the bar for precision. */
.progressiconic-wrapper > svg > g > path:first-of-type,
.progressiconic-wrapper > svg > path:first-of-type {
  fill: #EDE5EF;
  transition: fill 0.4s ease;
}
.progressiconic-wrapper[data-reached="1"] > svg:nth-of-type(-n+1) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="1"] > svg:nth-of-type(-n+1) > path:first-of-type,
.progressiconic-wrapper[data-reached="2"] > svg:nth-of-type(-n+2) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="2"] > svg:nth-of-type(-n+2) > path:first-of-type,
.progressiconic-wrapper[data-reached="3"] > svg:nth-of-type(-n+3) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="3"] > svg:nth-of-type(-n+3) > path:first-of-type,
.progressiconic-wrapper[data-reached="4"] > svg:nth-of-type(-n+4) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="4"] > svg:nth-of-type(-n+4) > path:first-of-type,
.progressiconic-wrapper[data-reached="5"] > svg:nth-of-type(-n+5) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="5"] > svg:nth-of-type(-n+5) > path:first-of-type {
  fill: #8E5C99;
}
/* The rightmost reached frame gets a touch of darker brand purple
 * so the eye lands clearly on "you reached here" rather than
 * sliding off the end of a uniform fill. */
.progressiconic-wrapper[data-reached="1"] > svg:nth-of-type(1) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="1"] > svg:nth-of-type(1) > path:first-of-type,
.progressiconic-wrapper[data-reached="2"] > svg:nth-of-type(2) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="2"] > svg:nth-of-type(2) > path:first-of-type,
.progressiconic-wrapper[data-reached="3"] > svg:nth-of-type(3) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="3"] > svg:nth-of-type(3) > path:first-of-type,
.progressiconic-wrapper[data-reached="4"] > svg:nth-of-type(4) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="4"] > svg:nth-of-type(4) > path:first-of-type,
.progressiconic-wrapper[data-reached="5"] > svg:nth-of-type(5) > g > path:first-of-type,
.progressiconic-wrapper[data-reached="5"] > svg:nth-of-type(5) > path:first-of-type {
  fill: #6E3E73;
}

/* Marker is no longer needed: the fill itself shows the score
 * position. Hidden rather than removed so legacy callers that
 * still mount ProgressMarker do not break. */
.progressiconic-wrapper .marker-wrapper {
  display: none;
}

/* Round the outer corners of the bar. The five frames sit flush
 * next to each other, so we only round the leftmost-left and
 * rightmost-right corners. overflow:hidden clips the SVG content
 * to the rounded box. */
.progressiconic-wrapper > svg:nth-of-type(1) {
  border-top-left-radius: 5px;
  border-bottom-left-radius: 5px;
  overflow: hidden;
}
.progressiconic-wrapper > svg:nth-of-type(5) {
  border-top-right-radius: 5px;
  border-bottom-right-radius: 5px;
  overflow: hidden;
}

/* Hide the smiley face white paths inside each frame. With the
 * monochrome look the faces would float on top as little white
 * shapes; we only want the rectangle. */
.progressiconic-wrapper > svg path[fill="white"],
.progressiconic-wrapper > svg path[fill="#FFFFFF"],
.progressiconic-wrapper > svg path[fill="#fff"] {
  display: none;
}

/* Drop the previous saturate filter and tier glows: monochrome bars
 * do not need de-saturating, and the high-score celebration is now
 * delivered by the Sparkle component below rather than a halo. */

/* ---- Sparkle for high scores ---------------------------------- */
/*
 * The Sparkle component is positioned absolutely in the top-right
 * of the bar. It uses the brand purple by default and goes warm
 * gold for Top Tier so it reads as a step up.
 */
.ts-sparkle {
  position: absolute;
  top: -8px;
  right: -8px;
  width: 14px;
  height: 14px;
  pointer-events: none;
  color: var(--primary-color);
}
.progressiconic-wrapper.progressiconic--top .ts-sparkle {
  /* Warm gold for the Top Tier celebration. */
  color: #C99A2E;
}
.ts-sparkle__star {
  display: block;
  animation: ts-sparkle-twinkle 1.8s ease-in-out infinite;
  transform-origin: center;
}

/* The sparkle scales and rotates gently to read as a twinkle
 * rather than a pulse. */
@keyframes ts-sparkle-twinkle {
  0%, 100% {
    transform: scale(0.85) rotate(0deg);
    opacity: 0.7;
  }
  50% {
    transform: scale(1.15) rotate(20deg);
    opacity: 1;
  }
}

/* Particle burst (Top Tier only): three small dots that fade
 * outward from the sparkle position. Each particle has its own
 * delay so they do not all fire at once. */
.ts-sparkle__particle {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: #C99A2E;
  opacity: 0;
  pointer-events: none;
}
.ts-sparkle__particle--a {
  animation: ts-sparkle-particle-a 2s ease-out infinite;
}
.ts-sparkle__particle--b {
  animation: ts-sparkle-particle-b 2s ease-out infinite;
  animation-delay: 0.3s;
}
.ts-sparkle__particle--c {
  animation: ts-sparkle-particle-c 2s ease-out infinite;
  animation-delay: 0.6s;
}
@keyframes ts-sparkle-particle-a {
  0% { transform: translate(-50%, -50%) translate(0, 0); opacity: 0.9; }
  100% { transform: translate(-50%, -50%) translate(10px, -10px); opacity: 0; }
}
@keyframes ts-sparkle-particle-b {
  0% { transform: translate(-50%, -50%) translate(0, 0); opacity: 0.9; }
  100% { transform: translate(-50%, -50%) translate(-12px, -6px); opacity: 0; }
}
@keyframes ts-sparkle-particle-c {
  0% { transform: translate(-50%, -50%) translate(0, 0); opacity: 0.9; }
  100% { transform: translate(-50%, -50%) translate(4px, 12px); opacity: 0; }
}

/* Honour reduced-motion: hold the sparkle at its rest state and
 * suppress particles. */
@media (prefers-reduced-motion: reduce) {
  .ts-sparkle__star {
    animation: none;
    transform: scale(1) rotate(0deg);
    opacity: 0.9;
  }
  .ts-sparkle__particle {
    display: none;
  }
}

/* TS-5190 / TS-5191 (styleguide button--go): the one-click "generate" buttons on
   the ideas pages are green (#178055) to signal a completed, one-press generate
   action, per the styleguide. Lives here - attached via global-styling - because
   the react_app bundle's own CSS is never loaded on the dashboard. Sits on top of
   .btn.btn-primary from PrimaryButton, so the compound selector clears it. */
.btn.ts-go-btn,
.btn.ts-go-btn:disabled {
  background-color: #178055;
  border-color: #178055;
  color: #fff;
  /* Uppercase Go-button label to match the extension/styleguide reference
     (TS-5188 QA). Done in CSS so the JSX text stays readable/translatable and
     both Go buttons ("Get video ideas" / "Get titles") stay consistent. */
  text-transform: uppercase;
  /* Center the inline SVG icon with the text: an inline <svg> aligns to the
     baseline and sits low, so flex-center the button content instead. The
     loader is a sibling of this button, not a child, so this is safe. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.btn.ts-go-btn:hover,
.btn.ts-go-btn:focus,
.btn.ts-go-btn:active {
  background-color: #178055;
  border-color: #178055;
  color: #fff;
  filter: brightness(0.92);
}

/* TS-5188 ideas pages: two-column card grid + carded idea boxes on the CMS
   light theme, matching the browser extension's idea cards. */
.ideas-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
  /* stretch so cards in the same row share the tallest card's height */
  align-items: stretch;
}
@media (max-width: 991.98px) {
  .ideas-grid {
    grid-template-columns: 1fr;
  }
}
.idea-card.card {
  background-color: #faf8fc;
  border: 1px solid #eae2f4;
  border-radius: 8px;
  margin-bottom: 0;
  /* fill the stretched grid cell so row cards are equal height */
  height: 100%;
}
/* More info / Less info: primary purple, not the default bootstrap-link blue. */
.btn.idea-card__toggle {
  color: var(--primary-color);
}
.btn.idea-card__toggle:hover,
.btn.idea-card__toggle:focus {
  color: #521f5a;
}
/* Centre the "More video/title ideas" button under the grid. */
.content-ideas__more,
.title-ideas__more {
  display: flex;
  justify-content: center;
}
.idea-card .idea-card__title {
  font-size: 1rem;
  font-weight: 700;
  color: #3e2155;
  margin-bottom: 12px;
}
.idea-card .idea-card__brief,
.idea-card .idea-card__method {
  font-size: 0.875rem;
  color: #6b6880;
}
.ideas-count {
  font-size: 0.875rem;
  color: #6b6880;
  margin-bottom: 12px;
}
/* Save = light-purple secondary sitting next to the solid-purple Refine, per the
   extension. Refine uses PrimaryButton (house purple), so no extra class.
   .ts-btn-soft-purple is the same treatment for page-level secondary actions,
   added when the "Ideas from my comments" generator needed one: this page is
   deliberately all-purple, so Bootstrap's .btn-secondary (orange #c2410c) is
   the wrong vocabulary here even though it is the house secondary elsewhere. */
.btn.idea-card__save,
.btn.ts-btn-soft-purple {
  background-color: #f2eef8;
  /* border-WIDTH, not just a colour. This rule set border-color alone, and
     width computes to 0, so the edge never existed: the pale fill measures
     around 1.1 against the surface behind it, well under the 3:1 boundary
     threshold, and the button read as a tinted label.
     #a27ea9 is the LIGHTEST point on the ramp from house purple to white that
     still clears 3:1 on both surfaces it appears against: 3.45 on the white
     card, 3.01 on the lavender page tint. Solved rather than picked, because
     the first value I chose by eye measured 2.23 and would have shipped a
     button with the same invisible edge in a slightly different colour. */
  border: 1px solid #a27ea9;
  color: var(--primary-color);
}
.btn.idea-card__save:hover,
.btn.idea-card__save:focus,
.btn.idea-card__save:active,
.btn.ts-btn-soft-purple:hover,
.btn.ts-btn-soft-purple:focus,
.btn.ts-btn-soft-purple:active {
  background-color: #e9e1f5;
  border-color: var(--primary-color, #672c72);
  color: var(--primary-color);
}

/* The page-level version needs two things the card Save does not.
 *
 * An edge. Measured on the Content Ideas panel, the pale fill sits at 1.08
 * against the surface behind it against a 3:1 boundary threshold, so the
 * button had no perceivable shape at all: it read as a label someone had
 * tinted. The palette standard calls this out as the threshold that gets
 * missed, because label-on-fill passes comfortably at 8.42 and reviewing only
 * that number says the button is fine. The border carries the shape, and the
 * purple clears 3:1 on both the panel and the page.
 *
 * Note the border-color the shared rule above sets does nothing on its own:
 * border-width computes to 0, so a colour with no width is invisible.
 *
 * And the same type treatment as the Go button next to it, which is uppercase.
 * Two buttons side by side in different cases read as two different kinds of
 * control rather than as a primary and its alternative.
 *
 * The states are listed because the shared hover rule above sets a pale
 * border-color at (0,2,1), which beats a plain (0,2,0) rule and would take the
 * edge away again the moment the pointer arrived. */
.btn.ts-btn-soft-purple,
.btn.ts-btn-soft-purple:hover,
.btn.ts-btn-soft-purple:focus,
.btn.ts-btn-soft-purple:active {
  border: 1px solid var(--primary-color, #672c72);
  text-transform: uppercase;
}


/* Danger button.
   The palette standard has red for destructive actions and ships the pair as
   --ts-btn-danger-bg/fg, but nothing in the SPA had a class that used them, so
   confirm-and-delete buttons wore the brand purple. Bootstrap's own .btn-danger
   is #dc3545, which is not the house value. Additive: only elements that opt in
   with this class change. */
.btn.ts-btn-danger,
.btn.ts-btn-danger:hover,
.btn.ts-btn-danger:focus,
.btn.ts-btn-danger:active {
  background-color: var(--ts-btn-danger-bg, #c62828);
  border-color: var(--ts-btn-danger-bg, #c62828);
  color: var(--ts-btn-danger-fg, #ffffff);
}
.btn.ts-btn-danger:hover,
.btn.ts-btn-danger:focus {
  filter: brightness(0.92);
}

/* Suggestion and story rows on /dashboard/user-facts.
   Small nested surfaces, so they follow the shortcut-row treatment rather
   than the panel one: opaque, no blur, and the squared corner. The state
   colour stays an accent (the section icon, the Answer button) and is not
   spent on a rail or a fill, per the card-idiom decision. */
.ts-fact-row {
  background: var(--ts-surface-card, #ffffff);
  border: 1px solid #e9e4ef;
  border-radius: 12px 12px 12px 3px;
  padding: 8px 12px;
  margin-bottom: 6px;
}

/* Link-styled buttons.
 *
 * The theme had no rule for .btn-link at all, so Bootstrap's stock #0d6efd
 * came through: a blue that appears nowhere in the brand. Seven controls
 * across five pages were showing it, everywhere the class is used bare rather
 * than paired with text-primary / text-muted / text-danger, which is how the
 * other five instances were accidentally covered.
 *
 * Brand purple, per the palette standard: purple is everything that is not
 * Go, secondary or danger. 8.73:1 on white and it holds on the frosted panel.
 * The underline stays, since without a fill or an edge it is the only thing
 * marking this as a control.
 *
 * Deliberately not touching the paired instances: text-* utilities are
 * !important and still win, so a control someone coloured on purpose keeps
 * its colour. */
.btn.btn-link {
  color: var(--primary-color, #672c72);
}

.btn.btn-link:hover,
.btn.btn-link:focus {
  color: var(--primary-hover, #521f5a);
}

/**
 * Projects list checklist pills (TS-5514).
 *
 * A class rather than inline styles so the pills get a hover state and sit
 * with the rest of the design system. Lives HERE and not in a stylesheet
 * imported from the React bundle: webpack emits main.min.css but nothing
 * attaches it, so bundle-imported CSS is silently dead on the dashboard
 * (see the note on this library in tubespanner_purple.libraries.yml).
 *
 * Colours come from the design tokens, never raw hex. Note that bootstrap's
 * .text-primary and .border-primary are still its default #0d6efd blue in
 * this theme - only .btn-primary is remapped to brand - so those utilities
 * must not be used to mean "brand".
 */
.ts-checklist-pill {
  display: inline-block;
  font: inherit;
  font-size: 11px;
  line-height: 14px;
  padding: 2px 7px;
  border-radius: 10px;
  white-space: nowrap;
  flex-shrink: 0;
  cursor: pointer;
  text-decoration: none;
  background: transparent;
  color: var(--ts-color-muted);
  border: 1px solid var(--ts-border-control);
}

.ts-checklist-pill:hover,
.ts-checklist-pill:focus-visible {
  color: var(--ts-btn-primary-bg);
  border-color: var(--ts-btn-primary-bg);
  text-decoration: none;
}

/* Done: filled brand. Purple rather than green, because green is the "go"
   action colour here and one colour with two meanings weakens both. */
.ts-checklist-pill--done {
  font-weight: 600;
  background: var(--ts-btn-primary-bg);
  color: var(--ts-btn-primary-fg);
  border-color: var(--ts-btn-primary-bg);
}

.ts-checklist-pill--done:hover,
.ts-checklist-pill--done:focus-visible {
  background: var(--ts-btn-primary-hover-bg);
  border-color: var(--ts-btn-primary-hover-bg);
  color: var(--ts-btn-primary-fg);
}

/* nowrap, NOT the shared .ts-pill-row utility: that wraps, and these read
   left-to-right as a sequence, so a ragged second line breaks the read. The
   column is sized to fit all four and the table scrolls horizontally. */
.ts-checklist-pill-row {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: var(--ts-space-1);
}
