/**
 * Local CAPTCHA widget — light/dark/RTL.
 * Mirrors reCAPTCHA v2 visual rhythm without external assets.
 */

.gdrfad-captcha {
  --lc-bg: #f9f9f9;
  --lc-fg: #1f2328;
  --lc-muted: #6b7280;
  --lc-border: #d0d7de;
  --lc-accent: #1a73e8;
  --lc-pass: #16a34a;
  --lc-fail: #dc2626;
  --lc-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 4px 12px rgba(0, 0, 0, .04);
  --lc-radius: 6px;

  display: inline-block;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
    'Helvetica Neue', Arial, sans-serif;
  font-size: 14px;
  color: var(--lc-fg);
  max-width: 320px;
  width: 100%;
}

@media (prefers-color-scheme: dark) {
  .gdrfad-captcha {
    --lc-bg: #1f2228;
    --lc-fg: #e5e9f0;
    --lc-muted: #9aa3b2;
    --lc-border: #3a4252;
    --lc-accent: #4f8df9;
    --lc-pass: #22c55e;
    --lc-fail: #f87171;
    --lc-shadow: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 16px rgba(0, 0, 0, .35);
  }
}

/* Box */
.gdrfad-captcha__box {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: var(--lc-bg);
  border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius);
  padding: 12px 14px;
  box-shadow: var(--lc-shadow);
}

/* Trigger button */
.gdrfad-captcha__checkbox {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  cursor: pointer;
  color: inherit;
  font: inherit;
  text-align: start;
  flex: 1;
  min-width: 0;
  outline-offset: 3px;
  border-radius: 4px;
}
.gdrfad-captcha__checkbox:focus-visible {
  outline: 2px solid var(--lc-accent);
}
.gdrfad-captcha__checkbox:disabled {
  cursor: not-allowed;
}

/* Escalated to the math question. The checkbox is inert from here on (the
   question is the only route to verified), so mute it and switch the box to
   a dashed outline — otherwise it reads as a live control that mysteriously
   refuses to tick. Opacity carries the cue in both light and dark themes. */
.gdrfad-captcha.is-fallback .gdrfad-captcha__checkbox {
  opacity: .55;
}
.gdrfad-captcha.is-fallback .gdrfad-captcha__check {
  border-style: dashed;
}

/* Checkbox box (the visual square that fills with the check or spinner) */
.gdrfad-captcha__check {
  position: relative;
  width: 28px;
  height: 28px;
  border: 2px solid var(--lc-border);
  border-radius: 4px;
  background: #fff;
  flex-shrink: 0;
  transition: border-color .25s ease, background .25s ease;
}
@media (prefers-color-scheme: dark) {
  .gdrfad-captcha__check { background: #2a2f3a; }
}

.gdrfad-captcha__check-icon {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transform: scale(.6);
  transition: opacity .2s ease, transform .25s cubic-bezier(.4, 1.4, .6, 1);
}
.gdrfad-captcha__check-path {
  stroke: #fff;
  stroke-dasharray: 30;
  stroke-dashoffset: 30;
  transition: stroke-dashoffset .35s ease .05s;
}

.gdrfad-captcha__spinner {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  border: 2px solid var(--lc-border);
  border-top-color: var(--lc-accent);
  opacity: 0;
  transition: opacity .2s ease;
  animation: lc-spin .9s linear infinite;
}

/* Label area */
.gdrfad-captcha__label {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.gdrfad-captcha__label-text {
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gdrfad-captcha__required {
  color: var(--lc-fail);
  font-weight: 700;
  margin-inline-start: 4px;
  user-select: none;
}
/* Status / error message — a full-width block BELOW the box, not a span
   squeezed into the button's label column (see the template comment). Sizing
   is deliberately independent of the box so a long message wraps naturally
   instead of forcing the widget taller. */
.gdrfad-captcha__status {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 6px 10px;
  border-radius: var(--lc-radius);
  /* Same reasoning as the cool-down chip below: this message lives OUTSIDE
     .gdrfad-captcha__box, so in OS dark mode on a light page it was painting
     the dark palette's red (#f87171) onto white — 2.76:1, well under the
     4.5:1 floor. Giving it the widget's own surface makes it legible in both
     themes on any page. */
  background: var(--lc-bg);
  border: 1px solid var(--lc-border);
  color: var(--lc-fail);
  font-size: 13px;
  line-height: 1.45;
  text-align: start;
  box-sizing: border-box;
}
/* Collapse entirely when empty so the widget keeps its resting height and
   nothing shifts until there is actually something to say. */
.gdrfad-captcha__status:empty {
  display: none;
  margin-top: 0;
  padding: 0;
  border: 0;
}

/* Brand */
.gdrfad-captcha__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font-size: 10px;
  line-height: 1.2;
  color: var(--lc-muted);
  text-align: center;
  flex-shrink: 0;
  user-select: none;
}
.gdrfad-captcha__brand-name {
  font-weight: 600;
  letter-spacing: .2px;
}
.gdrfad-captcha__shield {
  width: 28px;
  height: 28px;
  display: block;
  color: var(--lc-accent);
  transition: color .25s ease, transform .25s ease;
}
/* Shield turns green when the widget is verified, mirroring the checkbox. */
.gdrfad-captcha.is-pass .gdrfad-captcha__shield {
  color: var(--lc-pass);
  transform: scale(1.05);
}
.gdrfad-captcha.is-fail .gdrfad-captcha__shield {
  color: var(--lc-fail);
}

/* States */
.gdrfad-captcha.is-verifying .gdrfad-captcha__spinner { opacity: 1; }
.gdrfad-captcha.is-verifying .gdrfad-captcha__check { border-color: var(--lc-accent); }

.gdrfad-captcha.is-pass .gdrfad-captcha__check {
  background: var(--lc-pass);
  border-color: var(--lc-pass);
}
.gdrfad-captcha.is-pass .gdrfad-captcha__check-icon {
  opacity: 1;
  transform: scale(1);
}
.gdrfad-captcha.is-pass .gdrfad-captcha__check-path {
  stroke-dashoffset: 0;
}
.gdrfad-captcha.is-pass .gdrfad-captcha__label-text { color: var(--lc-pass); }

.gdrfad-captcha.is-fail .gdrfad-captcha__check {
  border-color: var(--lc-fail);
  animation: lc-shake .35s ease;
}
.gdrfad-captcha.is-fail .gdrfad-captcha__label-text { color: var(--lc-fail); }

/* Server-rejected — the form came back from validation with a message for
   this widget. Static (no animation): the page has just been re-rendered, so
   there is no transition for the eye to follow, and the border has to still
   be red when the user's gaze arrives. */
.gdrfad-captcha.is-error .gdrfad-captcha__box {
  border-color: var(--lc-fail);
}
.gdrfad-captcha.is-error .gdrfad-captcha__check {
  border-color: var(--lc-fail);
}

/* Attention pulse — fired when the user tries to submit without verifying.
   Reuses the shake animation, no permanent state change. */
.gdrfad-captcha.is-attention .gdrfad-captcha__box {
  animation: lc-shake .45s ease;
  border-color: var(--lc-fail);
}
.gdrfad-captcha.is-attention .gdrfad-captcha__check {
  border-color: var(--lc-fail);
}

/* Fallback */
.gdrfad-captcha__fallback {
  margin-top: 10px;
  background: var(--lc-bg);
  border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius);
  padding: 12px 14px;
  box-shadow: var(--lc-shadow);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
/* HTML's hidden attribute is otherwise overridden by display: flex above. */
.gdrfad-captcha__fallback[hidden] {
  display: none !important;
}
.gdrfad-captcha__fallback-prompt {
  font-size: 13px;
  color: var(--lc-muted);
}
.gdrfad-captcha__fallback-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.gdrfad-captcha__fallback-image {
  display: block;
  height: 48px;
  width: auto;
  max-width: 100%;
  border: 1px solid var(--lc-border);
  border-radius: 4px;
  background: #fff;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}
/* Audio alternative to the code image.
   Sized to the 44x44 minimum touch target rather than to the icon, because
   the people most likely to need it are also the most likely to be using
   assistive input or a shaky finger. */
.gdrfad-captcha__fallback-audio-btn {
  appearance: none;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--lc-border);
  border-radius: 4px;
  background: #fff;
  color: var(--lc-accent);
  cursor: pointer;
}
.gdrfad-captcha__fallback-audio-btn:hover { border-color: var(--lc-accent); }
.gdrfad-captcha__fallback-audio-btn:focus-visible {
  outline: 2px solid var(--lc-accent);
  outline-offset: 1px;
}
.gdrfad-captcha__fallback-audio-btn[aria-busy="true"] {
  opacity: 0.6;
  cursor: progress;
}
.gdrfad-captcha__fallback-audio-icon {
  width: 22px;
  height: 22px;
  fill: currentColor;
  stroke: currentColor;
}
/* The speaker glyph reads as directional. Under RTL it should point the
   same way as the rest of the layout, so mirror it with the page. */
[dir="rtl"] .gdrfad-captcha__fallback-audio-icon { transform: scaleX(-1); }
.gdrfad-captcha__fallback-audio-wave { stroke: currentColor; fill: none; }
@media (prefers-color-scheme: dark) {
  .gdrfad-captcha__fallback-audio-btn { background: #2a2f3a; }
}
@media (prefers-reduced-motion: reduce) {
  .gdrfad-captcha__fallback-audio-btn { transition: none; }
}

.gdrfad-captcha__fallback-input {
  flex: 1 1 110px;
  min-width: 90px;
  padding: 6px 10px;
  border: 1px solid var(--lc-border);
  border-radius: 4px;
  background: #fff;
  color: var(--lc-fg);
  font: inherit;
  /* The code is Latin characters in a fixed-width group; tracking them out
     makes each glyph easier to check against the image. */
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-variant-ligatures: none;
}
@media (prefers-color-scheme: dark) {
  .gdrfad-captcha__fallback-input { background: #2a2f3a; }
}
.gdrfad-captcha__fallback-input:focus-visible {
  outline: 2px solid var(--lc-accent);
  outline-offset: 1px;
  border-color: var(--lc-accent);
}
.gdrfad-captcha__fallback-submit {
  appearance: none;
  border: 0;
  padding: 6px 14px;
  background: var(--lc-accent);
  color: #fff;
  font: inherit;
  font-weight: 600;
  border-radius: 4px;
  cursor: pointer;
}
.gdrfad-captcha__fallback-submit:hover { filter: brightness(1.05); }
.gdrfad-captcha__fallback-submit:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.gdrfad-captcha__reload {
  appearance: none;
  background: transparent;
  border: 0;
  padding: 0;
  font-size: 12px;
  color: var(--lc-muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
}
.gdrfad-captcha__reload svg {
  width: 14px;
  height: 14px;
}
.gdrfad-captcha__reload:hover { color: var(--lc-accent); }

/* ── Invisible (V3-style) mode ──────────────────────────────────────────── */

/* Hide the checkbox box in invisible mode. The fallback panel still
   renders normally for the score-fail case. */
.gdrfad-captcha.is-mode-invisible .gdrfad-captcha__box {
  display: none;
}
/* Hide the badge in visible mode (default). */
.gdrfad-captcha:not(.is-mode-invisible) .gdrfad-captcha__badge {
  display: none;
}

/* Discreet "Protected by …" badge that replaces the checkbox in
   invisible mode. Inline, small, muted. */
.gdrfad-captcha__badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  line-height: 1.3;
  color: var(--lc-muted);
  user-select: none;
  padding: 4px 8px;
  border-radius: 4px;
  background: var(--lc-bg);
  border: 1px solid var(--lc-border);
  max-width: max-content;
}
.gdrfad-captcha__badge-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--lc-accent);
}
.gdrfad-captcha__badge-text {
  white-space: nowrap;
}
.gdrfad-captcha.is-mode-invisible.is-verifying .gdrfad-captcha__badge {
  border-color: var(--lc-accent);
}
.gdrfad-captcha.is-mode-invisible.is-pass .gdrfad-captcha__badge {
  border-color: var(--lc-pass);
  color: var(--lc-pass);
}
.gdrfad-captcha.is-mode-invisible.is-pass .gdrfad-captcha__badge-icon {
  color: var(--lc-pass);
}
.gdrfad-captcha.is-mode-invisible.is-fail .gdrfad-captcha__badge {
  border-color: var(--lc-fail);
  color: var(--lc-fail);
}

/* Honeypot — visually hidden but reachable to bots that read the DOM.
 *
 * Hidden by clipping, NOT by pushing off-canvas with `left: -10000px`.
 * Off-canvas positioning is direction-dependent: in LTR the browser clips
 * overflow past the left edge, but under `dir="rtl"` that same -10000px sits
 * in the *scrollable* direction, so it stretched the document to 10760px at a
 * 760px viewport and gave every Arabic page a horizontal scrollbar.
 *
 * The clip technique (Drupal core's own .visually-hidden) occupies no layout
 * space in either direction, so it cannot generate overflow — while keeping
 * the input present and fillable in the DOM, which is what makes the trap
 * work. The wrapper carries aria-hidden="true" so assistive tech skips it. */
.gdrfad-captcha__offscreen {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  border: 0 !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
}

/* Screen-reader live region */
.gdrfad-captcha__sr {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* RTL — flexbox handles row reversal automatically. The checkmark and the
   shield are universal ideograms and stay in their canonical orientation,
   so we deliberately do not flip them. */

/* Drupal admin theme dark mode (Claro) — matches when system not in dark. */
[data-drupal-admin-theme="claro"][data-drupal-color-scheme="dark"] .gdrfad-captcha,
.gin--dark-mode .gdrfad-captcha {
  --lc-bg: #1f2228;
  --lc-fg: #e5e9f0;
  --lc-muted: #9aa3b2;
  --lc-border: #3a4252;
  --lc-accent: #4f8df9;
}

@keyframes lc-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes lc-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  50% { transform: translateX(4px); }
  75% { transform: translateX(-2px); }
}

@media (prefers-reduced-motion: reduce) {
  .gdrfad-captcha__spinner { animation: none; }
  .gdrfad-captcha__check-icon { transition: none; }
  .gdrfad-captcha.is-fail .gdrfad-captcha__check { animation: none; }
  /* The attention pulse was missing from this list. It is the one animation
     a user can fire repeatedly — click Submit again, shake again — so it is
     the one that most needs an opt-out. */
  .gdrfad-captcha.is-attention .gdrfad-captcha__box,
  .gdrfad-captcha.is-attention .gdrfad-captcha__check { animation: none; }
}


/* ------------------------------------------------------------------ */
/* Rate-limit cool-down                                                */
/* ------------------------------------------------------------------ */

/* The visual half of the wait: an icon, a label and a ticking clock. The
   spoken half is the sentence in .gdrfad-captcha__status, which changes once
   a minute — see the template comment for why the two are separate. */
/* Carries its own surface rather than sitting on whatever the page happens
   to be. The widget's dark palette is switched by prefers-color-scheme, but
   only .gdrfad-captcha__box has a background to go with it — so in OS dark
   mode on a light-themed page (Olivero, and most Drupal front ends) anything
   outside that box was painting dark-mode text straight onto white. Measured
   at 1.21:1 for the timer. The chip puts it back on --lc-bg, where both
   themes are legible, and reads as a small panel under the box. */
.gdrfad-captcha__cooldown {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  padding: 6px 10px;
  border: 1px solid var(--lc-border);
  border-radius: var(--lc-radius);
  background: var(--lc-bg);
  font-size: 13px;
  line-height: 1.45;
  color: var(--lc-muted);
}
/* `display: flex` beats the user agent's [hidden] rule, so restate it. */
.gdrfad-captcha__cooldown[hidden] {
  display: none;
}
.gdrfad-captcha__cooldown-icon {
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  color: var(--lc-muted);
}
.gdrfad-captcha__cooldown-timer {
  /* Tabular figures and a floor on the width: without them the widget jitters
     once a second as the glyphs change width, and again when 10:00 becomes
     9:59. Deliberately NOT red — this is a wait, not an error, and the red
     headline above already carries that. */
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--lc-fg);
  min-width: 4ch;
  text-align: start;
}

/* The checkbox during a cool-down. `aria-disabled`, never `disabled`: a
   disabled button drops out of the tab order and takes its accessible
   description — which is where the wait is written — out of reach, and if it
   held focus the browser throws that focus to <body>. This way the user can
   tab back to it at any moment and hear how long is left.

   Muted with colour rather than opacity: opacity fades the :focus-visible
   outline along with everything else, and the focus ring has a contrast
   floor to meet. No pointer-events: none either — the click has to reach the
   handler so it can re-announce the remaining time. */
.gdrfad-captcha__checkbox[aria-disabled="true"] {
  cursor: not-allowed;
}
.gdrfad-captcha.is-cooldown .gdrfad-captcha__label-text {
  color: var(--lc-muted);
}
.gdrfad-captcha.is-cooldown .gdrfad-captcha__check {
  border-color: var(--lc-border);
  border-style: dashed;
}
.gdrfad-captcha.is-cooldown .gdrfad-captcha__shield {
  color: var(--lc-muted);
}
