/* ---------------------------------------------------------------------------
 * Palette
 *
 * Each token resolves in three steps: an explicit per-integration override, then
 * whatever design token the host page already publishes, then the widget's own
 * default. The widget used to hardcode a slate/blue Tailwind palette, so on any
 * site with a different accent it read as a foreign component pasted into the
 * page — on this one, a blue card inside a teal design.
 *
 * Adopting the host's tokens is also what makes it portable: drop it into a
 * Bootstrap page and it picks up --bs-*; drop it somewhere with no tokens at
 * all and the defaults below still give a complete, coherent widget.
 * ------------------------------------------------------------------------- */
.tmi-local-captcha {
  --tmi-captcha-bg: var(--tmi-captcha-bg-override, var(--surface-color, var(--bs-body-bg, #ffffff)));
  --tmi-captcha-surface: var(--tmi-captcha-surface-override, var(--bs-tertiary-bg, #f8fafc));
  --tmi-captcha-border: var(--tmi-captcha-border-override, var(--bs-border-color, #e2e8f0));
  --tmi-captcha-text: var(--tmi-captcha-text-override, var(--bs-body-color, #1e293b));
  --tmi-captcha-muted: var(--tmi-captcha-muted-override, var(--bs-secondary-color, #64748b));
  --tmi-captcha-accent: var(--tmi-captcha-accent-override, var(--accent-color, var(--bs-primary, #2563eb)));
  --tmi-captcha-success: var(--tmi-captcha-success-override, var(--bs-success, #16a34a));

  /* Derived from the accent so a host that only sets one variable still gets a
     consistent widget. color-mix has a fallback below for older engines. */
  --tmi-captcha-accent-soft: rgba(37, 99, 235, 0.12);
  --tmi-captcha-success-soft: rgba(22, 163, 74, 0.14);
  --tmi-captcha-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
  --tmi-captcha-radius: 0.875rem;

  /* Minimum hit area for anything interactive. WCAG 2.5.5 asks for 44x44 and
     the refresh control shipped at 36x36, the variant switch at 28 tall. */
  --tmi-captcha-touch-target: 2.75rem;

  position: relative;
  max-width: 400px;
  color: var(--tmi-captcha-text);
}

html[data-bs-theme="dark"] .tmi-local-captcha,
html[data-theme="dark"] .tmi-local-captcha,
[data-bs-theme="dark"] .tmi-local-captcha,
[data-theme="dark"] .tmi-local-captcha,
.tmi-local-captcha[data-theme="dark"] {
  --tmi-captcha-bg: var(--tmi-captcha-bg-override, var(--ambient-surface, var(--bs-body-bg, #1c2028)));
  --tmi-captcha-surface: var(--tmi-captcha-surface-override, var(--bs-tertiary-bg, #242a36));
  --tmi-captcha-border: var(--tmi-captcha-border-override, var(--bs-border-color, #334155));
  --tmi-captcha-text: var(--tmi-captcha-text-override, var(--bs-body-color, #e2e8f0));
  --tmi-captcha-muted: var(--tmi-captcha-muted-override, var(--bs-secondary-color, #94a3b8));
  --tmi-captcha-accent: var(--tmi-captcha-accent-override, var(--accent-color, var(--bs-primary, #60a5fa)));
  --tmi-captcha-success: var(--tmi-captcha-success-override, var(--bs-success, #4ade80));
  --tmi-captcha-accent-soft: rgba(96, 165, 250, 0.16);
  --tmi-captcha-success-soft: rgba(74, 222, 128, 0.16);
  --tmi-captcha-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}

/* The dark palette is selected by attribute, so it outranks a bare
   .tmi-local-captcha rule no matter where that rule sits in the file. The
   derivation therefore has to repeat those selectors, or the widget keeps its
   own blue tint on a dark host while the solid accent correctly turns teal. */
@supports (color: color-mix(in srgb, red, blue)) {
  .tmi-local-captcha,
  html[data-bs-theme="dark"] .tmi-local-captcha,
  html[data-theme="dark"] .tmi-local-captcha,
  [data-bs-theme="dark"] .tmi-local-captcha,
  [data-theme="dark"] .tmi-local-captcha,
  .tmi-local-captcha[data-theme="dark"] {
    --tmi-captcha-accent-soft: color-mix(in srgb, var(--tmi-captcha-accent), transparent 88%);
    --tmi-captcha-success-soft: color-mix(in srgb, var(--tmi-captcha-success), transparent 86%);
  }
}

.tmi-local-captcha__card {
  overflow: hidden;
  border: 1px solid var(--tmi-captcha-border);
  border-radius: var(--tmi-captcha-radius);
  background: var(--tmi-captcha-bg);
  box-shadow: var(--tmi-captcha-shadow);
}

.tmi-local-captcha__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.875rem 1rem;
  border-bottom: 1px solid var(--tmi-captcha-border);
  background: linear-gradient(180deg, var(--tmi-captcha-surface) 0%, var(--tmi-captcha-bg) 100%);
}

.tmi-local-captcha__title-wrap {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
}

.tmi-local-captcha__badge {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 0.5rem;
  background: var(--tmi-captcha-accent-soft);
  position: relative;
}

.tmi-local-captcha__badge::before {
  content: "";
  position: absolute;
  inset: 0.45rem 0.5rem 0.55rem;
  border: 2px solid var(--tmi-captcha-accent);
  border-radius: 0.2rem;
}

.tmi-local-captcha__badge::after {
  content: "";
  position: absolute;
  left: 0.72rem;
  top: 0.72rem;
  width: 0.35rem;
  height: 0.55rem;
  border-right: 2px solid var(--tmi-captcha-accent);
  border-bottom: 2px solid var(--tmi-captcha-accent);
  transform: rotate(45deg);
}

.tmi-local-captcha__title {
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.3;
}

.tmi-local-captcha__refresh {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--tmi-captcha-border);
  border-radius: 0.625rem;
  background: var(--tmi-captcha-surface);
  color: var(--tmi-captcha-muted);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease, transform 0.15s ease;
}

.tmi-local-captcha__refresh:hover {
  color: var(--tmi-captcha-accent);
  border-color: var(--tmi-captcha-accent);
  background: var(--tmi-captcha-accent-soft);
}

.tmi-local-captcha__refresh:focus-visible,
.tmi-local-captcha__variant:focus-visible,
.tmi-local-captcha__puzzle-cell:focus-visible,
.tmi-local-captcha__input:focus-visible {
  outline: 3px solid var(--tmi-captcha-accent-soft);
  outline-offset: 2px;
  border-color: var(--tmi-captcha-accent);
}

.tmi-local-captcha__refresh:active {
  transform: scale(0.96);
}

.tmi-local-captcha__refresh-icon {
  display: block;
  width: 1rem;
  height: 1rem;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36'/%3E%3Cpath d='M21 3v6h-6'/%3E%3C/svg%3E") center / contain no-repeat;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M21 12a9 9 0 1 1-2.64-6.36'/%3E%3Cpath d='M21 3v6h-6'/%3E%3C/svg%3E") center / contain no-repeat;
}

.tmi-local-captcha__body {
  padding: 1rem;
}

.tmi-local-captcha__challenge {
  display: flex;
  justify-content: center;
  margin-bottom: 0.625rem;
}

.tmi-local-captcha__status {
  min-height: 1.15rem;
  margin-bottom: 0.5rem;
  font-size: 0.78rem;
  color: var(--tmi-captcha-muted);
}

.tmi-local-captcha__label {
  display: grid;
  gap: 0.35rem;
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--tmi-captcha-text);
}

.tmi-local-captcha__image {
  display: block;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--tmi-captcha-border);
  border-radius: 0.625rem;
  background: var(--tmi-captcha-surface);
  user-select: none;
  -webkit-user-drag: none;
}

.tmi-local-captcha__puzzle-block {
  width: 100%;
  max-width: 320px;
  margin-inline: auto;
}

.tmi-local-captcha__puzzle-caption {
  margin: 0 0 0.5rem;
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.35;
  color: var(--tmi-captcha-text);
}

.tmi-local-captcha__puzzle-wrap {
  /* Must match what LocalCaptchaPuzzleRenderer actually emits:
     width  = GRID_COLS * cellW + pad*2 = 4*76 + 16 = 320
     height = topH + GRID_ROWS * cellH + pad*2 = 54 + 3*72 + 16 = 286
     The height was 262 here, 24px short of the real image. The PNG was squashed
     into the wrong box, and every clickable cell — positioned as a percentage of
     this reference — drifted away from the shape it was meant to cover, worsening
     down the grid. Taps near a boundary hit the neighbouring tile. */
  --puzzle-ref-w: 320;
  --puzzle-ref-h: 286;
  --puzzle-pad: 8;
  --puzzle-header: 54;

  /* Space below the last drawn row, which is NOT the same as --puzzle-pad.
     The renderer sizes the image as topH + rows*cellH + pad*2 but only spends
     pad on the left and right and starts the rows at topH exactly, so the extra
     8px all ends up under the grid. Stretching the overlay to --puzzle-pad made
     it 224 tall over 216 of drawn rows, and the 8px surplus spread across three
     rows as a downward drift of the hit areas. */
  --puzzle-footer: 16;
  position: relative;
  width: 100%;
  max-width: 320px;
  aspect-ratio: var(--puzzle-ref-w) / var(--puzzle-ref-h);
  flex-shrink: 0;
  margin-inline: auto;
}

.tmi-local-captcha__puzzle-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: fill;
  border-radius: 0.625rem;
}

.tmi-local-captcha__puzzle-grid {
  --puzzle-cols: 4;
  --puzzle-rows: 3;
  position: absolute;
  left: calc(var(--puzzle-pad) / var(--puzzle-ref-w) * 100%);
  right: calc(var(--puzzle-pad) / var(--puzzle-ref-w) * 100%);
  top: calc(var(--puzzle-header) / var(--puzzle-ref-h) * 100%);
  bottom: calc(var(--puzzle-footer) / var(--puzzle-ref-h) * 100%);
  display: grid;
  grid-template-columns: repeat(var(--puzzle-cols), minmax(0, 1fr));
  grid-template-rows: repeat(var(--puzzle-rows), minmax(0, 1fr));
  gap: 0;
  pointer-events: none;
}

.tmi-local-captcha__puzzle-cell {
  position: relative;
  box-sizing: border-box;
  width: calc(100% - 2px);
  height: calc(100% - 2px);
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  justify-self: center;
  align-self: center;
  border: 0;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
  border-radius: 0.375rem;
  transition: box-shadow 0.15s ease, background-color 0.15s ease;
}

.tmi-local-captcha__puzzle-cell:hover {
  box-shadow: inset 0 0 0 2px var(--tmi-captcha-accent);
  background: var(--tmi-captcha-accent-soft);
}

.tmi-local-captcha__puzzle-cell.is-selected {
  box-shadow: inset 0 0 0 2px var(--tmi-captcha-success);
  background: var(--tmi-captcha-success-soft);
}

.tmi-local-captcha.is-loading .tmi-local-captcha__card {
  opacity: 0.82;
}

.tmi-local-captcha__math {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 4.5rem;
  width: 100%;
  padding: 1rem 1.25rem;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  border: 1px dashed var(--tmi-captcha-border);
  border-radius: 0.625rem;
  background: var(--tmi-captcha-surface);
}

/* The dots show position at a glance; the count states it plainly. Four
   unlabelled dots never said how many picks were expected, and the element was
   aria-hidden in full, so assistive tech got nothing at all. */
.tmi-local-captcha__progress-dots {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
}

.tmi-local-captcha__progress-count {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--tmi-captcha-muted);
}

.tmi-local-captcha__progress {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.tmi-local-captcha__progress-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 999px;
  background: var(--tmi-captcha-border);
  transition: background-color 0.15s ease, transform 0.15s ease;
}

.tmi-local-captcha__progress-dot.is-active {
  background: var(--tmi-captcha-accent);
  transform: scale(1.15);
}

.tmi-local-captcha__progress-dot.is-done {
  background: var(--tmi-captcha-success);
}

.tmi-local-captcha__input-wrap .form-control {
  border-color: var(--tmi-captcha-border);
  background: var(--tmi-captcha-surface);
  color: var(--tmi-captcha-text);
}

.tmi-local-captcha__input-wrap .form-control:focus {
  border-color: var(--tmi-captcha-accent);
  box-shadow: 0 0 0 0.2rem var(--tmi-captcha-accent-soft);
}

.tmi-local-captcha__footer {
  padding: 0 1rem 1rem;
}

.tmi-local-captcha__hint {
  margin: 0 0 0.75rem;
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--tmi-captcha-muted);
}

/* ---------------------------------------------------------------------------
 * Reading order
 *
 * The instruction ("click the tiles in the order shown above") lived in the
 * footer, i.e. after the grid: a visitor met the task before being told the
 * rules and had to look back up to make sense of it. Telling people what to do
 * before showing them the thing to do it with is the whole point.
 *
 * Done by promoting the footer's children into the card's flex flow rather than
 * moving markup, so the JS keeps every selector it queries. Where
 * display: contents is unsupported the layout simply falls back to today's
 * order — nothing breaks, it just stays as it was.
 * ------------------------------------------------------------------------- */
@supports (display: contents) {
  .tmi-local-captcha__card {
    display: flex;
    flex-direction: column;
  }

  .tmi-local-captcha__header {
    order: 1;
  }

  .tmi-local-captcha__footer {
    display: contents;
  }

  .tmi-local-captcha__hint {
    order: 2;
    margin: 0;
    padding: 0.75rem 1rem 0;
  }

  .tmi-local-captcha__body {
    order: 3;
  }

  /* The alternative-format switch stays last: it is an escape hatch, offered
     once the visitor has seen what they are being asked to do. */
  .tmi-local-captcha__a11y {
    order: 4;
    padding: 0 1rem 1rem;
  }
}

/* ---------------------------------------------------------------------------
 * Inside a host dialog
 *
 * The Resume gate already gives the dialog a shield icon, the heading
 * "Проверка безопасности" and the line "complete the task below to confirm you
 * are human". The widget then repeated a third variation of the same sentence
 * in its own header. On a phone that pushed the actual task below the fold for
 * no information gained.
 *
 * The refresh control lives in that header, so only the redundant title goes;
 * the header itself stays and collapses to the action alone.
 * ------------------------------------------------------------------------- */
.resume-captcha-overlay .tmi-local-captcha__title-wrap {
  display: none;
}

.resume-captcha-overlay .tmi-local-captcha__header {
  /* With the title gone the header is a single action, so it drops its divider
     and gradient rather than reading as an empty band above the content. */
  justify-content: flex-end;
  padding-block: 0.5rem;
  border-bottom: 0;
  background: none;
}

/* ---------------------------------------------------------------------------
 * One line for the instruction and the refresh action
 *
 * A lone button on its own row above a lone paragraph spent a full row of a
 * dialog that is already tall on a phone. The two belong together anyway: the
 * sentence says what to do, the button says "give me a different one".
 *
 * Grid rather than flex because the body and the alternative-format switch must
 * still span the full width beneath them, which a single flex row cannot do
 * without extra wrappers the JS would have to know about.
 * ------------------------------------------------------------------------- */
@supports (display: grid) {
  .resume-captcha-overlay .tmi-local-captcha__card {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: start;
  }

  .resume-captcha-overlay .tmi-local-captcha__hint {
    grid-column: 1;
    grid-row: 1;
    align-self: center;
    padding-block: 0;
    padding-inline-end: 0;
  }

  .resume-captcha-overlay .tmi-local-captcha__header {
    grid-column: 2;
    grid-row: 1;
    padding-inline-start: 0.5rem;
  }

  .resume-captcha-overlay .tmi-local-captcha__body,
  .resume-captcha-overlay .tmi-local-captcha__a11y {
    grid-column: 1 / -1;
  }
}

.tmi-local-captcha__a11y {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.tmi-local-captcha__variant {
  padding: 0.35rem 0.65rem;
  border: 1px solid var(--tmi-captcha-border);
  border-radius: 999px;
  background: transparent;
  color: var(--tmi-captcha-muted);
  font-size: 0.75rem;
  line-height: 1.2;
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}

.tmi-local-captcha__variant:hover {
  color: var(--tmi-captcha-accent);
  border-color: var(--tmi-captcha-accent);
  background: var(--tmi-captcha-accent-soft);
}

.tmi-local-captcha__hp {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.tmi-local-captcha.is-loading {
  opacity: 0.72;
  pointer-events: none;
}

.tmi-captcha-fallback-note {
  border-radius: 0.375rem;
}

/* ---------------------------------------------------------------------------
 * Input modality
 *
 * Split by how the visitor points rather than by viewport width. A 13" laptop
 * with a touchscreen and a 6" phone need the same generous hit areas; a compact
 * desktop window does not. Width alone answers the wrong question.
 * ------------------------------------------------------------------------- */

/* Coarse pointer: finger or stylus. */
@media (pointer: coarse) {
  .tmi-local-captcha__refresh,
  .tmi-local-captcha__variant {
    min-width: var(--tmi-captcha-touch-target);
    min-height: var(--tmi-captcha-touch-target);
  }

  .tmi-local-captcha__variant {
    padding-block: 0.625rem;
  }

  .tmi-local-captcha__puzzle-cell {
    /* A tile has to stay comfortably tappable even when four sit per row. */
    min-height: var(--tmi-captcha-touch-target);
  }

  /* Hover styling is meaningless without a hover-capable pointer and leaves a
     tile looking stuck in a highlighted state after a tap. */
  .tmi-local-captcha__puzzle-cell:hover,
  .tmi-local-captcha__refresh:hover,
  .tmi-local-captcha__variant:hover {
    background: inherit;
  }

  /* Touch scrolling should never be interrupted by a double-tap zoom on a
     control the visitor is trying to press. */
  .tmi-local-captcha__puzzle-cell,
  .tmi-local-captcha__refresh,
  .tmi-local-captcha__variant {
    touch-action: manipulation;
  }
}

/* Fine pointer: mouse or trackpad. Hover affordances belong here only. */
@media (hover: hover) and (pointer: fine) {
  .tmi-local-captcha__puzzle-cell {
    transition: transform 120ms ease, box-shadow 120ms ease;
  }

  .tmi-local-captcha__puzzle-cell:hover {
    transform: translateY(-1px);
    box-shadow: 0 4px 12px var(--tmi-captcha-accent-soft);
  }
}

/* ---------------------------------------------------------------------------
 * Width
 * ------------------------------------------------------------------------- */

/* Narrow viewports: fill the available width instead of leaving the card
   stranded at 400px inside a full-bleed mobile dialog. */
@media (max-width: 30rem) {
  .tmi-local-captcha {
    max-width: 100%;
  }

  .tmi-local-captcha__header,
  .tmi-local-captcha__body,
  .tmi-local-captcha__footer {
    padding-inline: 0.75rem;
  }

  .tmi-local-captcha__puzzle-cell {
    border-radius: 0.25rem;
  }

  /* Header actions wrap under the title rather than squeezing it to nothing,
     but stay pinned to the trailing edge so a wrapped button does not end up
     stranded on the left of an otherwise empty row. */
  .tmi-local-captcha__header {
    flex-wrap: wrap;
  }

  .tmi-local-captcha__header > :last-child {
    margin-inline-start: auto;
  }
}

/* Wide viewports: the card can afford a little more presence. */
@media (min-width: 48rem) {
  .tmi-local-captcha {
    max-width: 26rem;
  }
}

/* ---------------------------------------------------------------------------
 * Preferences and capabilities
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .tmi-local-captcha *,
  .tmi-local-captcha *::before,
  .tmi-local-captcha *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media (prefers-contrast: more) {
  .tmi-local-captcha {
    --tmi-captcha-border: currentColor;
  }

  .tmi-local-captcha__card {
    border-width: 2px;
  }
}

/* Windows high-contrast mode replaces every colour, so the widget has to lean
   on system keywords or its structure disappears entirely. */
@media (forced-colors: active) {
  .tmi-local-captcha__card,
  .tmi-local-captcha__puzzle-cell {
    border: 1px solid CanvasText;
  }

  .tmi-local-captcha__puzzle-cell[aria-pressed="true"],
  .tmi-local-captcha__puzzle-cell.is-selected {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }

  .tmi-local-captcha__refresh:focus-visible,
  .tmi-local-captcha__variant:focus-visible {
    outline: 2px solid Highlight;
  }
}

/* Print: a captcha is meaningless on paper and only wastes ink. */
@media print {
  .tmi-local-captcha {
    display: none !important;
  }
}
