/*
 * Full-screen search takeover.
 *
 * Constrained to the shell width rather than the window, same as the story
 * viewer: on a phone that is genuinely full-bleed, and on desktop it keeps the
 * mini app inside its own frame instead of taking over the browser tab.
 *
 * Two layers, and they stay apart for the same reason the viewer's do — they
 * animate different properties at different moments:
 *
 *   .search-overlay          the fade. Also the ground, so nothing of the page
 *                            behind shows through mid-transition.
 *   .search-overlay__sheet   the travel. Entry rise and dismiss drag are the
 *                            same axis, so they compose into one translate
 *                            rather than fighting over `transform`.
 *
 * Timing comes off the shared scale: --duration with --easing, which is what
 * INTERACTIONS.md files a modal appearing under ("Ease for pure appear /
 * disappear; things that are not moving do not need overshoot"). The 220ms the
 * design called for is inside the noise of the app's own 180ms fade, and a
 * fourth duration on the scale would cost more than those 40ms buy.
 */

.search-overlay {
  /* Distance the sheet rises through on entry. Small on purpose: the overlay
     is arriving, not being thrown. */
  --enter-y: 12px;

  position: fixed;
  inset: 0;
  /*
   * Above every page the app bar can open it from — the fixture page's 80, the
   * club page's 95, the league page's 97, the player page's 98 — and under the
   * story viewer's 100. The viewer and this never coexist, but if that ever
   * changes the viewer is the surface opened *from* this one and has to win.
   * It was 90 while the home screen was the only place with a search button;
   * a player page's search that opened *behind* the player page was the
   * failure that moved it.
   */
  z-index: 99;
  display: flex;
  max-width: var(--app-max-width);
  margin-inline: auto;
  background: var(--bg);
  opacity: 0;
  transition: opacity var(--duration) var(--easing);
}

/* A class beats the UA [hidden] rule, so display has to be turned off by hand. */
.search-overlay[hidden] {
  display: none;
}

.search-overlay[data-open="true"] {
  --enter-y: 0px;

  opacity: 1;
}

.search-overlay__sheet {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  padding-top: calc(var(--space-7) + var(--safe-top));
  /*
   * Entry and dismiss on one property. The drag writes --drag-y directly and
   * the entry rise is a token the open state zeroes, so a dismiss that starts
   * mid-entry adds to the rise instead of cancelling it.
   */
  transform: translateY(calc(var(--enter-y) + var(--drag-y, 0px)));
  transition: transform var(--duration) var(--easing);
}

/* Under the finger, nothing eases — the sheet is being held, not animated. */
.search-overlay[data-dragging="true"] .search-overlay__sheet {
  transition: none;
}

/*
 * A drag that ended where it started springs home, which is exactly what an
 * overshoot is for. Its own state rather than the resting "false": the entry
 * rise rests in the same place and must *not* overshoot — a fullscreen surface
 * that bounces past its resting position on the way in, with a keyboard about
 * to come up under it, is the one thing this transition has to avoid.
 */
.search-overlay[data-dragging="released"] .search-overlay__sheet {
  transition: transform var(--spring-duration) var(--spring);
}

/* --- Header --------------------------------------------------------------- */

/*
 * Back arrow, then the field. --space-3 is the app bar's own margin and the
 * fixtures list's padding, so every edge in the app stays on one line whether
 * the overlay is up or not.
 *
 * The row owns the dismiss drag (see SearchOverlay.js), which is why selection
 * is off here — left on, the browser starts a native selection drag and fires
 * pointercancel mid-gesture, and on iOS a long press raises the callout menu
 * instead of reaching the handler. The input opts back in below; it is excluded
 * from the gesture precisely so its text stays selectable.
 */
.search-overlay__header {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding-inline: var(--space-3);
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/*
 * Bare, like the app bar's own buttons. .icon-btn is a grey chip built to lift
 * off the white page, and the field beside it is already the card in this row —
 * a second one would be two cards deep for one control.
 */
.search-overlay__back {
  flex: none;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-full);
  color: var(--text);
}

.search-overlay__back:active {
  background: var(--surface-sunken);
}

/*
 * The same card the app bar wears — grey fill, hairline, floating shadow — so
 * the overlay reads as the bar having grown into the screen rather than as a
 * different surface arriving. A sunken fill would have been ungrounded here:
 * this sits on the white page, not inside a card.
 */
.search-field {
  flex: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-2) var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-floating);
}

/* 
 * The focus ring goes on the card, not on the input inside it. base.css draws
 * :focus-visible as an offset outline, which on a bare input nested in a
 * bordered field paints a second rounded box a couple of pixels inside the
 * first — it reads as a rendering fault rather than as focus. The border
 * picking up the accent is the same "this one is active" signal every other
 * selected thing in the app uses, and :focus-within catches the touch and mouse
 * cases too, which is right for a field: the caret is in it either way.
 */
.search-field:focus-within {
  border-color: var(--accent);
}

.search-field__input:focus-visible {
  outline: none;
}

.search-field__icon {
  color: var(--text-muted);
}

.search-field__input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  /* The placeholder names three things to search and has to hold one line
     beside the back arrow at the shell's narrowest — --text-md overruns it. */
  font-size: var(--text-sm);
  /* Explicit, against the header's blanket `none` above — this is the one thing
     in that row a caret belongs in. */
  user-select: text;
  -webkit-user-select: text;
}

.search-field__input::placeholder {
  color: var(--text-muted);
}

/* A darker fill inside a grey card, which is what --surface-sunken is for.
   Small and quiet: it is a correction, not an action. */
.search-field__clear {
  flex: none;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  background: var(--surface-sunken);
  border-radius: var(--radius-full);
  color: var(--text-secondary);
}

.search-field__clear[hidden] {
  display: none;
}

/* --- Tabs ----------------------------------------------------------------- */

/* A class beats the UA [hidden] rule, so display has to be turned off by hand. */
.search-tabs[hidden] {
  display: none;
}

/*
 * The gap is deliberately zero: the two tabs divide the row between them, so
 * they tile it and their underlines meet at the midpoint. A gap here would open
 * a strip of hairline that belongs to neither tab.
 */
.search-tabs {
  position: relative;
  flex: none;
  display: flex;
  margin-top: var(--space-4);
  padding-inline: var(--space-3);
  border-bottom: 1px solid var(--border);
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/*
 * Colour carries the whole active state, exactly as it does in the bottom nav
 * and the date strip. The weight deliberately does not change with it: the
 * underline is placed from the tabs' measured widths, and a semibold swap would
 * resize both of them on every switch — the problem the nav solves with an
 * invisible ghost label, and which simply does not arise if the weight holds.
 */
.search-tab {
  /*
   * Zero basis, so the row is split evenly rather than by label length — the
   * date strip's own arrangement, and for the same reason: on `auto`,
   * "Suggested" would take half again as much as "Recent" and the pair would
   * stop reading as one set of equal choices. It also makes the underline's
   * scaleX a no-op today, since both tabs measure the same; the scale stays in
   * layoutIndicator() so a third tab, or a label that changes, still lands.
   */
  flex: 1 1 0;
  min-width: 0;
  padding: var(--space-2) var(--space-1) var(--space-3);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: color var(--duration) var(--easing);
}

.search-tab[aria-selected="true"] {
  color: var(--accent);
}

/*
 * Sits on the row's own hairline. Width is set once from the widest tab and
 * every switch is translateX + scaleX off a left origin, so the slide never
 * touches layout. --spring rather than a plain ease: INTERACTIONS.md files tab
 * indicators as objects that move, and this is the bottom nav's indicator
 * redrawn as a rule.
 */
.search-tabs__indicator {
  position: absolute;
  bottom: -1px;
  left: 0;
  height: 2px;
  border-radius: var(--radius-full);
  background: var(--accent);
  opacity: 0;
  transform-origin: left center;
  pointer-events: none;
}

/* Added one frame after the first placement so it does not fly in from 0. */
.search-tabs--ready .search-tabs__indicator {
  opacity: 1;
  transition:
    transform var(--spring-duration) var(--spring),
    opacity var(--duration) var(--easing);
}

/* --- Body ----------------------------------------------------------------- */

.search-overlay__body {
  flex: 1;
  overflow-y: auto;
  /* The page behind is already scroll-locked; this stops a flick at either end
     of the list handing the gesture back to it anyway. */
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-block: var(--space-4)
    calc(var(--space-6) + max(var(--safe-bottom), var(--tg-chrome-inset, 0px)));
}

.search-panel[hidden] {
  display: none;
}

.search-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/*
 * The cascade. 40ms per section, top to bottom, off the shared --stagger-step —
 * the story ring's draw-on already uses it, so the two reveals are the same
 * hand. It runs on the panel's content rather than on the overlay, so switching
 * tabs replays it and a swap never lands as a hard cut.
 */
.search-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  animation: search-section-in var(--duration) var(--easing) backwards;
  animation-delay: calc(var(--i, 0) * var(--stagger-step));
}

@keyframes search-section-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

.search-section__title {
  padding-inline: var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  letter-spacing: -0.01em;
}

/* --- Card rows ------------------------------------------------------------ */

/*
 * Full-bleed scrollers: the track carries the inset so the first card lines up
 * with the section title above it and the last one can still scroll clear of
 * the screen edge — the league strip's own arrangement.
 */
.search-cards__track {
  display: flex;
  gap: var(--space-2);
  width: max-content;
  padding-inline: var(--space-3);
}

.search-card {
  --press-scale: 0.92;

  flex: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  width: 72px;
}

.search-card:focus-visible {
  border-radius: var(--radius-md);
}

/*
 * The badge disc: white with a hairline, the same container the league strip
 * gives its avatars, so a crest is framed the same way wherever it appears. The
 * crest inside keeps its own shape — square for a club or competition, round
 * for a player — which is the only thing that differs between the rows.
 */
.search-card__media {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.search-card__media--round {
  border-radius: var(--radius-full);
}

/*
 * Two lines, then it clips. The cards divide a fixed width between them, so a
 * long club name has to wrap rather than widen its card — and a third line
 * would make every card in the row as tall as the wordiest one.
 */
.search-card__label {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
  color: var(--text-secondary);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
  line-height: 1.25;
  text-align: center;
}

/* --- Ranking tiles -------------------------------------------------------- */

/*
 * Destinations, not entities — so these get the box the entity cards
 * deliberately do not. Two of them, side by side, on the card treatment the app
 * bar and the date strip already share.
 */
.search-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
  padding-inline: var(--space-3);
}

.search-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  min-width: 0;
  padding: var(--space-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  text-align: left;
}

/* Darker fill inside a grey card — which is exactly what --surface-sunken is for. */
.search-tile__media {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  background: var(--surface-sunken);
  border-radius: var(--radius-full);
  color: var(--text-secondary);
}

.search-tile__label {
  max-width: 100%;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

/* --- Rows ----------------------------------------------------------------- */

/*
 * Hairlines between rows, no fill and no box around the group — the fixtures
 * list's own arrangement, and for the same reason: a long list has to scan as
 * one column rather than as a stack of cards the eye enters one at a time.
 */
.search-rows__item + .search-rows__item,
.search-region + .search-region {
  border-top: 1px solid var(--border);
}

.search-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2) var(--space-3);
  text-align: left;
}

.search-row:active {
  background: var(--surface-sunken);
}

.search-row__glyph {
  color: var(--text-muted);
}

.search-row__identity {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.search-row__name {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

.search-row__subtitle {
  color: var(--text-muted);
  font-size: var(--text-xs);
}

/* --- All competitions ----------------------------------------------------- */

.search-region {
  /* The panel animates grid rows, which reflows every frame — containment
     scopes that reflow to this region alone, so opening one country does not
     make the browser re-check the hundred under it. Same reason .league has it. */
  contain: layout paint;
}

.search-region__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  text-align: left;
}

.search-region__header:active {
  background: var(--surface-sunken);
}

.search-region__name {
  flex: 1;
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}

/*
 * Points right when closed and down when open — the fixtures accordion's own
 * scheme, not a 180deg flip. Two expandable lists in one app that disagree
 * about which way a chevron turns is the kind of drift the token files exist to
 * prevent, and it costs nothing to match.
 */
.search-region__chevron {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  color: var(--text-muted);
  transition: transform var(--duration) var(--easing);
}

.search-region[data-collapsed="true"] .search-region__chevron {
  transform: rotate(-90deg);
}

/* 0fr -> 1fr gives the animated open without ever measuring a height. */
.search-region__panel {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--duration) var(--easing);
}

.search-region[data-collapsed="true"] .search-region__panel {
  grid-template-rows: 0fr;
}

.search-region__panel-inner {
  min-height: 0;
  overflow: hidden;
}

/* Indented under their country, and separated from its header by the hairline
   the rows already carry between themselves. */
.search-region__panel-inner .search-rows {
  border-top: 1px solid var(--border);
  padding-left: var(--space-5);
}

/* --- Page behind ---------------------------------------------------------- */

/*
 * The page must not scroll under the overlay. Position is preserved on the
 * document, which is what lets the close land the user exactly where they were.
 */
body[data-search-open] {
  overflow: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .search-section {
    animation: none;
  }
}
