@import "tailwindcss";

/* Geist, variable — one file covers every weight, 68 KB. Checked against the
   Serbian alphabet before it was chosen: Č č Ć ć Š š Ž ž Đ đ are all present.
   Đ (U+0110) is the one that fashionable typefaces usually drop, and a site that
   cannot write Đorđe is not a Serbian site. SIL Open Font License. */
@font-face {
  font-family: "Geist";
  src: url("/assets/Geist-Variable-b11a6485.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/assets/GeistMono-Variable-6dd1dc09.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@theme {
  --font-sans: "Geist", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "Geist Mono", ui-monospace, "SF Mono", monospace;

  /* Warm-neutral greys, pale end first, the way every other scale runs: 50 is
     the page and 950 is the type. */
  --color-carbon-50: #f7f7f5;
  --color-carbon-100: #f0f0ed;
  --color-carbon-150: #e9e9e5;
  --color-carbon-200: #dededa;
  --color-carbon-300: #cbcbc6;
  --color-carbon-400: #b0b0aa;
  --color-carbon-500: #8a8a85;
  --color-carbon-600: #6b6b66;
  --color-carbon-700: #51514d;
  --color-carbon-800: #3a3a37;
  --color-carbon-900: #262624;
  --color-carbon-950: #141413;

  /* One accent: navy. Chosen off five blues walked through on real pages. It
     carries the headline, the primary action, the current page and the active
     tab.

     Three variables rather than two: the shadow under the button has to be the
     accent at low alpha, and a hex cannot be given one, so --accent-rgb holds
     the same colour as bare channels for rgb(var(--accent-rgb) / 0.35). Keep
     them in step if the colour ever moves again. */
  --color-accent: #1e3a8a;
  --color-accent-deep: #162d6e;
  --accent-rgb: 30 58 138;

  /* Medals are content, not theme. Gold-silver-bronze stays on the podium, but
     muted to metal rather than paint — the taxi yellow went with the old accent. */
  --color-medal-gold: #d9b24c;
  --color-medal-silver: #c9c9c4;
  --color-medal-bronze: #b58157;

  --color-flame-500: #e8112d;
  --color-sky-500: #0e9fd8;

  /* The four disciplines, so a list of five hundred can be read down by what
     was shot rather than by reading every line.

     Low saturation on purpose: the page is warm paper and the accent is navy,
     and four loud hues beside them would turn a list into confetti. Each one
     is the colour of the place rather than a label picked off a wheel — a hall
     under lights, an open sky, grass, the woods a 3D course runs through.

     Two pairs had to be pushed apart. Outdoor sits beside the navy that marks
     an event abroad, so it leans teal rather than blue; and 3D sits beside the
     amber of a hall, so it leans red rather than brown. Both pairs turn up on
     the same line often enough to matter. */
  --color-indoor: #9a6b1e;
  --color-outdoor: #15788f;
  --color-field: #43773a;
  --color-three-d: #a8452c;

  /* What a template actually means — the text colour, a hairline, a wash — so
     the markup never bakes in a value it cannot defend. Ink is graphite, not
     black: the same family as the button, one step darker than the body, so the
     page is soft without going grey. */
  --color-ink: #33332f;
  --color-line: rgba(0, 0, 0, 0.1);
  --color-wash-1: rgba(0, 0, 0, 0.02);
  --color-wash-2: rgba(0, 0, 0, 0.045);
  --color-wash-3: rgba(0, 0, 0, 0.08);
}


@layer base {
  html {
    scroll-behavior: smooth;
    background-color: var(--color-carbon-50);
  }

  body {
    font-feature-settings: "cv11", "ss01";
  }

  ::selection {
    background-color: var(--color-accent);
    color: #ffffff;
  }

  :focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
  }
}

/* Declared with @utility rather than as plain classes, because Tailwind 4 will
   only let @apply reach a real utility — and the components below apply them. */

/* Every number on this site is a measurement compared against the number above
   it — a score, a rank, a date. Tabular lining figures keep the columns true,
   and the mono face says "this is data" before anyone has read a digit. */
@utility num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: -0.01em;
}

/* Headlines only. At display sizes the default tracking opens up and the words
   stop holding together as a shape. */
@utility display {
  letter-spacing: -0.035em;
  line-height: 1.02;
}

/* The weave itself. One 20 px tile from app/assets/images/carbon.svg, repeated.
   Kept as a file rather than inlined so the pattern stays legible and editable —
   it is a drawing, and a drawing encoded into a CSS string stops being one. */
@utility carbon-weave {
  background-image: url("/assets/carbon-b5ce3b4d.svg");
  background-size: 20px 20px;
  background-repeat: repeat;
}

@layer components {
  /* The surface the whole archive is printed on: almost black where the reading
     happens, turning into carbon as it falls away.

     Lit from one corner rather than evenly. An even vignette reads as a vignette
     — a photographic effect applied to a page. A single light reads as a surface
     with a direction, which is what a bow limb under a lamp actually looks like.

     Two layers, both fixed: the page moves, the surface it is printed on does
     not. The colour lives on <html>, because giving <body> a background of its
     own paints straight over z-index -1 — which is exactly how this was
     invisible the first time.

     ::before is the weave, held back by two masks at once. The ellipse keeps it
     out of the middle where the reading happens; the reach keeps it near the
     lamp. Intersected, so fibre survives only where both agree — at an edge, and
     in the light. One mask alone gives either a symmetrical ring or a woven page.

     The reach is radial from the corner, not a diagonal ramp. A ramp treats the
     other two corners as half-lit, because they are halfway along it — and they
     came out visibly brighter than they should be. Light falls off with distance
     from where it is, so the far corners have to be dark in every direction at
     once, which only a radial does.

     Two lamps: the main one top right, and a much fainter one top left — enough
     that the far side is not dead, not enough to read as a second source. They are added to
     each other first and the edge is intersected with the sum, so fibre needs an
     edge and at least one lamp. Composited in that order because a mask layer
     combines with everything listed after it, not before. */
  .carbon-surface::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image: url("/assets/carbon-b5ce3b4d.svg");
    background-size: 20px 20px;
    -webkit-mask-image:
      radial-gradient(ellipse 82% 74% at 50% 42%, transparent 28%, rgba(0, 0, 0, 0.55) 66%, #000 100%),
      radial-gradient(72% 68% at 99% -2%, rgba(0, 0, 0, 0.72) 0%, rgba(0, 0, 0, 0.3) 32%, transparent 62%),
      radial-gradient(44% 42% at 0% 2%, rgba(0, 0, 0, 0.17) 0%, rgba(0, 0, 0, 0.07) 32%, transparent 62%);
    mask-image:
      radial-gradient(ellipse 82% 74% at 50% 42%, transparent 28%, rgba(0, 0, 0, 0.55) 66%, #000 100%),
      radial-gradient(72% 68% at 99% -2%, rgba(0, 0, 0, 0.72) 0%, rgba(0, 0, 0, 0.3) 32%, transparent 62%),
      radial-gradient(44% 42% at 0% 2%, rgba(0, 0, 0, 0.17) 0%, rgba(0, 0, 0, 0.07) 32%, transparent 62%);
    -webkit-mask-composite: source-in, source-over;
    mask-composite: intersect, add;
    pointer-events: none;
  }

  /* ::after is the light itself. On a pale page the bloom is nearly invisible —
     the lamp shows through where the weave gathers — and the shade opposite has
     to stay a breath, not a stain: what reads as shadow on black reads as dirt
     on white. */
  .carbon-surface::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background-image:
      radial-gradient(60% 48% at 94% -8%, rgba(255, 255, 255, 0.042), transparent 58%),
      radial-gradient(38% 32% at 2% 0%, rgba(255, 255, 255, 0.008), transparent 60%),
      radial-gradient(95% 75% at 20% 112%, rgba(0, 0, 0, 0.13), transparent 60%);
    pointer-events: none;
  }

  /* The quiet workhorse: a row in a list of results. The hover is a wash rather
     than a border so a long list does not jump by a pixel as the mouse crosses
     it. */
  .row {
    @apply -mx-3 flex items-baseline gap-4 rounded-lg px-3 py-2.5 transition-colors;
  }

  .row:hover {
    @apply bg-wash-2;
  }

  /* Rank badges. Gold, silver and bronze are the one piece of visual shorthand
     every single visitor already knows, archer or not. */
  .rank {
    @apply num inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-medium;
    @apply bg-wash-2 text-carbon-600;
  }

  .rank-1 { @apply bg-medal-gold text-carbon-950; }
  .rank-2 { @apply bg-medal-silver text-carbon-950; }
  .rank-3 { @apply bg-medal-bronze text-carbon-50; }

  .field {
    @apply rounded-lg border border-carbon-200 bg-white px-3.5 py-2 text-sm text-carbon-800 shadow-xs transition;
    @apply placeholder:text-carbon-500;
  }

  .field:focus {
    @apply border-accent outline-none;
  }

  /* Dark select menus still draw their options from the system, so the options
     themselves have to be told as well or they come out white-on-white. */
  .field option {
    @apply bg-carbon-150 text-carbon-900;
  }

  /* The primary action is ink — dark and colourless. The crimson stays for the
     small signals (the eyebrow, the disputed flag, focus); an action does not
     need a colour, it needs weight. The inset highlight along the top edge and
     the shadow are what separate a button from a rectangle filled with dark:
     lit from above, resting on the page. */
  /* The primary action wears the accent — that is what makes it the primary
     action. The inset highlight along the top edge and the tinted shadow are
     what separate a button from a rectangle filled with colour: lit from above,
     resting on the page. */
  .btn {
    @apply rounded-lg bg-accent px-4 py-2 text-sm font-semibold text-white transition-all;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgb(var(--accent-rgb) / 0.35);
  }

  .btn:hover {
    @apply bg-accent-deep;
  }

  .btn:active {
    box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
    transform: translateY(0.5px);
  }

  /* Pagination. pagy marks the current page with aria-current and the gaps with
     role=separator, so the state is styled off what the markup already says
     rather than off classes it does not have. */
  .pagy {
    @apply num flex flex-wrap items-center gap-1 text-sm;
  }

  .pagy a {
    @apply rounded-lg px-3 py-1.5 text-carbon-600 transition-colors;
  }

  .pagy a[href]:hover {
    @apply bg-wash-2 text-ink;
  }

  .pagy a[aria-current="page"] {
    @apply bg-accent font-medium text-white;
  }

  .pagy a[role="separator"] {
    @apply text-carbon-400;
  }

  /* A panel that sits above the surface: slightly lighter, hairline edge. No
     shadow — on a black page a shadow is invisible and only costs paint. */
  .panel {
    @apply rounded-2xl border border-carbon-200/70 bg-white shadow-xs;
  }

  /* The filter form goes the other way: a shade *into* the page rather than
     above it. The controls inside it are white, so they read as the things you
     reach for, and the results below stay what the eye lands on. No shadow —
     a recessed surface does not cast one. */
  .filters {
    @apply rounded-2xl border border-carbon-200/70 bg-wash-2;
  }

  /* What standing a tournament has, so a list of five hundred can be read down
     rather than across. Three, because that is how the federation keeps them.

     Only the two that are not the ordinary case carry colour: 386 of 552 are
     recognised domestic events, and painting the majority says nothing. The
     accent marks an event shot abroad; the muted one marks a result the
     federation does not count, which is the thing worth knowing before you
     read the score. */
  .standing {
    @apply rounded-full px-2.5 py-0.5 text-xs font-medium whitespace-nowrap;
  }

  /* What was shot, which is the one thing on this list worth colouring. The
     tint is the colour at a tenth, so the tag reads as a wash the type sits
     in rather than as a block of paint. */
  .discipline {
    @apply rounded-full px-2.5 py-0.5 text-xs font-medium whitespace-nowrap;
  }

  .discipline-target_indoor {
    @apply bg-indoor/10 text-indoor;
  }

  .discipline-target_outdoor {
    @apply bg-outdoor/10 text-outdoor;
  }

  .discipline-field {
    @apply bg-field/10 text-field;
  }

  .discipline-three_d {
    @apply bg-three-d/10 text-three-d;
  }

  /* 'other', and the four tournaments that never said. Shape without colour:
     a grey tag beside four coloured ones reads as 'not one of those', which
     is what it is. */
  .discipline-other,
  .discipline-unknown {
    @apply bg-wash-2 text-carbon-600;
  }

  .standing-official {
    @apply bg-wash-2 text-carbon-700;
  }

  .standing-international {
    @apply bg-accent/10 text-accent;
  }

  .standing-unofficial {
    @apply bg-wash-2 text-carbon-500 italic;
  }
}
