/* css/main/competition-sheet.css — part 24/30 of the app styles (was css/main.css; loaded in this order by index.html). */
      /* ═══════════════════════════════════════════════════════════════
         THE FULL COMPETITION SHEET (phase 3b, 2026-08-26)
         ⚠️ SAME FAMILY AS THE DIPLOMA VIEWER, for the same reason: it takes
         TWO classes (`.modal-overlay.cd-visor`) to beat
         `.modal-overlay.open`, which sets `display:flex` and would paint it
         as a bottom sheet. Here the content is long and is read in full.
         ═══════════════════════════════════════════════════════════════ */
      .modal-overlay.cd-visor {
        position: fixed;
        inset: 0;
        z-index: 1200;
        background: var(--bg);
        display: flex;
        flex-direction: column;
        align-items: stretch;
        overflow: hidden;
        padding-top: env(safe-area-inset-top, 0px);
      }
      .cd-bar {
        flex: 0 0 auto;
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 10px 12px;
        border-bottom: 1px solid var(--border-soft);
        background: var(--bg);
      }
      .cd-back { width: auto; min-height: 44px; padding: 8px 16px; }
      .cd-bar-t {
        flex: 1 1 auto;
        min-width: 0;
        font-family: "Oswald", sans-serif;
        font-size: 14px;
        color: var(--muted);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
      /* ⛔ NO `-webkit-overflow-scrolling: touch`, and this is not tidying: it
         IS the bug. It is deprecated and causes the "I reach the bottom and
         it won't scroll back up" on iOS (the same thing removed from
         `.wizvis-scroll`), and here it had crept back in. Santi found it on
         2026-08-27 scrolling a competition sheet to the very end.
         `overscroll-behavior:contain` cuts scroll chaining to the document
         behind; `touch-action:pan-y` ensures the vertical gesture;
         `min-height:0` lets the flex child shrink, without which it scrolls
         nothing. */
      .cd-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        touch-action: pan-y;
        padding: 18px 16px calc(32px + env(safe-area-inset-bottom, 0px));
      }
      /* The reading column does not stretch on a tablet or PC: a 900 px line
         of text is not readable. */
      .cd-scroll > * { max-width: 560px; margin-left: auto; margin-right: auto; }
      .cd-mapa { flex: 1 1 auto; min-height: 0; background: var(--surface2); }

      .cd-hero { display: flex; justify-content: center; margin-bottom: 18px; }
      .cd-head { margin-bottom: 16px; }
      .cd-title {
        margin: 6px 0 8px;
        font-family: "Oswald", sans-serif;
        font-size: 24px;
        line-height: 1.2;
        color: var(--text);
        word-break: break-word;
      }
      .cd-org { font-size: 13px; color: var(--muted); margin-bottom: 2px; word-break: break-word; }
      /* ⛔ THERE USED TO BE A FOURTH PALETTE HERE, the clearest case of drift
         in the whole 2026-08-27 sweep: this strip painted air in GOLD,
         firearms in RED, clay in GREEN and historic arms in GREY (the brand,
         error, success and muted colours) while the list filter, the chips
         and the card used the four discipline tokens.
         The same competition changed colour when its sheet opened. It now uses
         `.tl-fil`, like everything else; only its padding remains here. */
      .cd-head { padding-left: 12px; }

      .cd-actions { margin: 14px 0 4px; }
      .cd-yain {
        margin: 12px 0 8px;
        padding: 10px 12px;
        border-radius: 10px;
        font-size: 14px;
        line-height: 1.45;
        border: 1px solid var(--border);
        background: var(--surface);
        color: var(--text);
      }
      /* ⛔ Colour is NEVER the only cue: the sentence inside says whether you
         are pre-registered or registered. The border only reinforces it. */
      .cd-yain-ins { border-color: var(--success); }
      .cd-yain-pre { border-color: var(--accent); }

      /* ══════════════════════════════════════════════════════════════
         THE SHEET, FROM THE HEADER DOWN — redone on 2026-09-01

         ⛔ What was there, in Santi's words: "from the button down everything
         is mere text, some aligned left, others right, the age and shooter
         categories are textual, it's hard to see whether they are really for
         me or not". And it was literal: `.cd-v` had `text-align: right`, the
         label on the left and the value on the right, so a long value ("Yes,
         counts for the organisation's annual ranking") broke into two
         lines misaligned against the edge. Nothing grouped the sections: they
         were loose headings and paragraphs on the background.

         ⭐ This is the screen where someone decides WHETHER TO SIGN UP. It has
         to be scannable, not read in full.

         Three changes, none touching the logic:
          1 · Each section is a card. It gives the blocks the eye needs
              to jump to its own part.
          2 · The value is aligned LEFT, under a fixed-width label. No more
              two alignments on the same line.
          3 · Categories and ages stop being a comma-separated list and
              become separate chips, which lets you answer "is mine
              there?" at a glance.
         ══════════════════════════════════════════════════════════════ */
      .cd-sec {
        margin-top: 14px;
        padding: 14px;
        border: 1px solid var(--border-soft);
        /* ⚠️ 10 px, the CARD radius of the house scale (6 control ·
           10 card · 14 dialog · 999 pill). 12 was written here and
           `tests/unit-escala-visual.test.js` caught it, which is exactly the
           kind of drift that guard exists to stop: two pixels that
           nobody sees separately and that, repeated, make the app
           look made by three different hands. */
        border-radius: 10px;
        background: var(--surface);
      }
      /* ⭐ THE BLOCK HEADING: ACCENT + RULE. Chosen by Santi among three
         proposals seen rendered (2026-09-01). His brief, in his own
         words: "something that DELIMITS, that HIGHLIGHTS that title", and
         "without being excessively flashy". Each half does one of the two:
         the colour highlights, the rule delimits.

         ⛔ The other two were discarded, and it is worth recording why: the
         vertical accent bar added a second gold piece to a
         card that already carries chips with their own border (two elements
         for the same job), and the tinted top band, which delimits best,
         turns into EIGHT colour stripes running down the sheet,
         exactly what he asked to avoid. One looks very good; eight
         in a row are heavy. */
      .cd-h2 {
        margin: 0 0 10px;
        padding-bottom: 8px;
        border-bottom: 1px solid var(--border-soft);
        font-family: "Oswald", sans-serif;
        font-size: 13px;
        letter-spacing: 1px;
        text-transform: uppercase;
        color: var(--accent);
      }
      .cd-desc { font-size: 14px; line-height: 1.55; color: var(--text); white-space: pre-line; }
      .cd-rows { display: flex; flex-direction: column; gap: 10px; }
      /* ⛔ Grid, not `flex` with the value pushed to the right. The label
         takes a fixed column and the value ALWAYS starts in the same place,
         so the rows of a section read like a table. */
      /* ⚠️ The label column is ELASTIC, not a fixed width with a
         breakpoint. It started as `96px` with a `@media (max-width:
         340px)` that stacked it, and that had two drawbacks: the breakpoint
         looks at the VIEWPORT (not the room the sheet really has left, since
         it also lives inside its margins) and it cannot be checked with
         headless Chrome, which on this machine does not go below 503 px wide.
         A `clamp` adapts by itself and there is nothing to verify at a
         specific width: at 390 px it gives the usual 96 px and on a narrow
         device it shrinks instead of strangling the value column. */
      .cd-row {
        display: grid;
        grid-template-columns: clamp(64px, 27%, 96px) 1fr;
        gap: 4px 12px;
        align-items: baseline;
        font-size: 14px;
      }
      .cd-k {
        color: var(--muted);
        font-size: 12px;
        line-height: 1.45;
      }
      .cd-v { color: var(--text); text-align: left; word-break: break-word; line-height: 1.45; }
      /* Data chips: SAME family as the discipline chips in the
         header (`.cc-chip`), in a neutral tone. The ones above use the accent
         because they say what the competition is about; if these were the
         same, the whole sheet would be gold and none would stand out. */
      .cd-chips { display: flex; flex-wrap: wrap; gap: 6px; }
      /* ⛔ NEVER `var(--surface-2)`: the app does NOT define that token (Score
         and the directory have it, not the app) and the fallback would have
         left the chip the SAME colour as the card containing it, i.e.
         invisible except for its border. A `var()` that does not resolve
         raises no error.
         ⛔⭐ THIS NOTE WAS WRITTEN HERE AND NOT APPLIED TO THE REST: on
         2026-09-12 **nine more uses** of `var(--surface-2)` were measured in
         this same file, all painting a TRANSPARENT background without anyone
         seeing it. It is now watched by `tema-tokens-check.py`, which besides
         comparing the two themes checks that every `var(--x)` WITHOUT a
         fallback is defined here.
         ⛔⛔ And THE NINE WERE CHANGED TO `var(--surface2)`, WITHOUT the
         hyphen, the token the app DOES have (`:185` and `:309`) and which I
         did not see when fixing it: I put an invented `color-mix` that in dark
         gave almost the same but in light came out **neutral grey** in a theme
         that is warm beige. The closing code review caught it. The difference
         between the two names is a hyphen, and it is exactly the kind of
         detail for which one should check whether the token already exists
         BEFORE making one up.
         ⚠️ This chip is the exception and keeps its `color-mix`: its decision
         is earlier and its own (a custom tone so it is not confused with the
         card containing it), it did not come from `--surface-2`. */
      .cc-chip-n {
        color: var(--text);
        background: color-mix(in srgb, var(--text) 7%, var(--surface));
        border-color: var(--border);
      }
      /* ⭐ THE SHOOTER'S OWN. Santi's brief: "make my category stand out".
         With the others in grey, theirs jumps out without searching for it.
         ⛔ And colour is NOT the only thing that distinguishes it (house rule,
         and here it truly matters: a colour-blind person must be able to
         answer "is mine there?" just as fast). It carries a checkmark,
         `title` and `aria-label`. The accent / `--accent-fg` pair is the same
         as the primary buttons, so contrast is already solved in both
         themes. */
      .cc-chip-yo {
        color: var(--accent-fg);
        background: var(--accent);
        border-color: var(--accent);
        font-weight: 700;
        display: inline-flex;
        align-items: center;
        gap: 5px;
      }
      .cd-dim { font-size: 13px; color: var(--muted); line-height: 1.45; }
      .cd-dim-b { margin-bottom: 8px; }
      .cd-list { margin: 0; padding-left: 18px; font-size: 14px; line-height: 1.6; color: var(--text); }

      .cd-campo { padding: 10px 0; border-top: 1px solid var(--border-soft); }
      .cd-campo:first-child { border-top: 0; padding-top: 0; }
      .cd-campo-nm { font-weight: 600; font-size: 14px; color: var(--text); }
      .cd-campo-addr { font-size: 13px; color: var(--muted); margin-top: 2px; }
      .cd-inst { margin: 6px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.5; color: var(--text); }
      .cd-campo-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
      .cd-campo-btn { width: auto; min-height: 44px; padding: 8px 14px; font-size: 13px; }
      /* #223: "Add to my calendar", with its icon in front (not an emoji). */
      .cd-cal-btn { display: inline-flex; align-items: center; gap: 8px; }
      .cd-cal-btn svg { flex: none; }

      .cd-dia { padding: 8px 0; border-top: 1px solid var(--border-soft); }
      .cd-dia:first-child { border-top: 0; padding-top: 0; }
      .cd-dia-hd { font-weight: 600; font-size: 14px; color: var(--text); }
      .cd-tanda { font-size: 13px; color: var(--text); margin-top: 3px; }
      .cd-prereg { margin-top: 10px; font-size: 13px; color: var(--text); }

      /* The card that opens the sheet behaves as tappable, but its buttons
         still take precedence (the handler ignores taps on them). */
      .cc-card[data-ficha] { cursor: pointer; }
      .cc-card[data-ficha]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

      /* ══════════════════════════════════════════════════════════════
         IDENTITY PROTECTION NOTICE — the three notices (2026-08-30)

         ⛔ REPLACES A PATCH-UP. What was there before were hard-coded colours
         (#c8a84b on #0d0d0d), typography inside the `style` attribute and not
         a single class, so in the LIGHT theme it was unreadable, the same
         failure as the close button of the photo viewer. Everything now comes
         from the palette.

         ⭐ TWO TONES, and it is the only thing that makes it decent to insist
         on someone who did listen to us: `es-alarma` when they have NO safety
         net (shield in the alert colour) and without it when only one more is
         missing (icon of the path itself, in accent). If someone who enabled
         Drive gets, four days later, another screen with a danger triangle,
         what they learn is to close notices without reading them. */
      .prot-ov {
        position: fixed; inset: 0;
        /* ⚠️ 800, THE VALUE IT ALREADY HAD, and not `var(--z-lock)`: the
           `--z-*` scale exists in Score but **the app does not have it yet**
           (see the notes at lines ~3757 and ~6259). A `var()` that does not
           resolve leaves the property invalid, i.e. the layer WITHOUT
           z-index: it would slip under anything. When the app has the
           scale, this is a `lock`. */
        z-index: 800;
        background: var(--bg); overflow-y: auto;
        /* ⛔ No `-webkit-overflow-scrolling: touch`: deprecated, and the
           direct cause of the iOS "I reach the bottom and it won't scroll
           back up" (`scripts/scroll-overlay-check.py`). The previous notice
           had it. */
        overscroll-behavior: contain;
      }
      .prot-caja {
        min-height: 100%;
        display: flex; flex-direction: column;
        align-items: center; justify-content: center;
        gap: 18px;
        max-width: 460px; margin: 0 auto;
        padding: 32px 20px 40px;
        text-align: center;
      }
      .prot-ic { color: var(--accent); }
      .prot-ov.es-alarma .prot-ic { color: var(--alerta); }
      .prot-ic svg { width: 64px; height: 64px; display: block; }
      .prot-t {
        font-family: "Oswald", sans-serif;
        font-size: 20px; line-height: 1.25; color: var(--text);
      }
      .prot-s {
        font-size: 14px; line-height: 1.6; color: var(--text-dim);
        white-space: pre-line;
        /* The reading measure: without it the paragraph stretches on a tablet
           and becomes uncomfortable on exactly the screen that must be read
           in full. */
        max-width: 42ch;
      }
      .prot-ops { width: 100%; }
      /* The path, only one per notice. It is the house's main button with its
         icon in front: no new family is needed for a button. */
      .prot-ir {
        width: 100%; min-height: 52px;
        display: flex; align-items: center; justify-content: center; gap: 10px;
        padding: 14px 18px; border-radius: 10px;
        background: var(--accent); border: 1px solid transparent;
        color: var(--accent-fg);
        font-family: "Oswald", sans-serif; font-size: 14px; font-weight: 600;
        letter-spacing: .04em; text-transform: uppercase;
        cursor: pointer; transition: opacity .15s;
      }
      .prot-ir:hover { opacity: .9; }
      .prot-ir:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
      .prot-ir-ic { flex: 0 0 auto; display: inline-flex; }
      .prot-ir-ic svg { width: 20px; height: 20px; }
      /* ⛔ "Not now" must be tappable without searching for it (otherwise the
         notice is a trap) but without competing with the path. Flat and
         muted, and with its 44 px of finger room. */
      .prot-luego {
        width: 100%; min-height: 44px;
        background: none; border: 0; color: var(--muted);
        font: inherit; font-size: 13px; cursor: pointer;
      }
      .prot-luego:hover { color: var(--text); }
      .prot-luego:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
      @media (prefers-reduced-motion: reduce) { .prot-ir { transition: none; } }

      /* The verify-a-diploma button, ABOVE "My diplomas" (Santi,
         2026-09-24: at the bottom it kept shifting with each new diploma).
         Separated from the list by a line: it is an action, not one more
         card. */
      .dip-verificar {
        margin-bottom: 16px;
        padding-bottom: 16px;
        border-bottom: 1px solid var(--border-soft);
      }
      /* ⛔ IT IS AN ACTION, NOT ONE MORE DIPLOMA, and that is the whole
         difference from the cards above. Same family (`.hist-card`, the one
         that already paints each diploma on this screen) plus the language of
         "this is something you do": DASHED border, which `.photo-pick-btn`
         already uses for choosing, and the icon in accent.
         ⭐ What is gained by reusing the card: the title is in normal case and
         WRAPS (before it was uppercase and overflowed the button on a phone),
         the help stops being a loose paragraph below and becomes the second
         line of the card, and the chevron says something opens. */
      .dip-verif { border-style: dashed; }
      .dip-verif .dip-verif-ic {
        flex: 0 0 auto; display: inline-flex; color: var(--accent);
      }
      .dip-verif .dip-verif-ic svg { width: 22px; height: 22px; }
      /* `.hist-main` and `.hist-name` are `<div>` in the diploma cards and
         here `<span>`: a `<button>` cannot contain a `<div>` without the
         browser pulling it out of the button. Block display is restored by
         hand. */
      .dip-verif .hist-main, .dip-verif .hist-name, .dip-verif .hist-meta {
        display: block;
      }

      /* ═══════════════════════════════════════════════════════════════
         "WHAT HAPPENED", inside each competition's card
         (2026-08-26). It used to be its own screen with a menu entry; now it
         is a dropdown where the shooter is already looking at that
         competition.
         ═══════════════════════════════════════════════════════════════ */
      .mov-wrap { margin-top: 10px; }
      /* Reuses the draw button (`.mc-sorteo-b`), which is already this
         house's dropdown: same height, same chevron, same rotation. */
      .mov-b { width: auto; }
      .mov-b[aria-expanded="true"] .mov-chev { transform: rotate(180deg); }
      .mov-chev { transition: transform 0.18s ease; margin-left: 4px; }
      @media (prefers-reduced-motion: reduce) { .mov-chev { transition: none; } }
      .mov-slot:empty { display: none; }
      .mov-caja {
        margin-top: 8px;
        padding: 10px 12px;
        border: 1px solid var(--border-soft);
        border-radius: 10px;
        font-size: 13px;
        color: var(--muted);
        line-height: 1.45;
      }
      .mov-linea {
        position: relative;
        margin: 10px 2px 0;
        padding-left: 22px;
      }
      .mov-linea::before {
        content: "";
        position: absolute;
        left: 5px;
        top: 4px;
        bottom: 4px;
        width: 2px;
        background: var(--border);
      }
      /* The history row wraps the button that jumps to the spectator view: a
         button inside another is not valid HTML, so they are siblings. */
      .hist-fila { margin-bottom: 10px; }
      .hist-fila .mov-wrap { margin: 6px 0 0 2px; }

      /* The "Links" section: what belongs to no competition. */
      .mov-vinc { margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--border-soft); }
      /* ⛔ Except on its own screen, where it hangs from the button that opens
         it and goes at the very TOP (2026-08-27). There the rule and the 24 px
         of air were separating it from nothing: what sits above it is the
         button that opened it. */
      .mcl-mov-wrap .mov-vinc { margin-top: 0; padding-top: 0; border-top: 0; }
      .mcl-mov-wrap .mov-ent:first-child { margin-top: 8px; }
      /* ⛔ AIR BELOW, closed and open (Santi, 2026-08-27): *"it's very
         close to the three counters"*. They are two different zones (what
         HAPPENED and what THERE IS) and when the button moved up here they
         ended up at 0 px, reading as a single block. It goes on the wrapper,
         not on the button or the panel, so the spacing is the same whether
         expanded or not. */
      .mcl-mov-wrap { margin-bottom: 20px; }
      /* The affiliation dialog (integration code + channels, 2026-10-04).
         The error sits right under the field and keeps its height so the
         channels don't jump when it appears. */
      .vd-err { color: var(--warn); font-size: 13px; line-height: 1.5; min-height: 18px; margin-top: 6px; }
      .vd-canales-t { margin-top: 14px; }
      /* On launch, why the code is being asked for now (2026-10-07). */
      .vd-intro { font-size: 14px; line-height: 1.5; color: var(--text); margin: 4px 0 12px; }
      #vd-exp, #vin-integ-nota { margin-top: 8px; }
      /* The row's integration line: the stored code is read, not decorated. */
      .mcl-integ { font-size: 12px; color: var(--text); margin-top: 4px; word-break: break-all; }
      .mov-ent { margin-top: 12px; }
      .mov-ent-t { font-size: 14px; font-weight: 600; color: var(--text); word-break: break-word; }

      /* ═══════════════════════════════════════════════════════════════
         THE TYPE RULE — a single class for the whole app (2026-08-27)

         ⛔ Santi asked for it on seeing that in "Search" the FILTER was in its
         colour and the result CARD came out grey: *"the block, the same"*.
         Rule §8-bis c-bis had been written looking at the filters and stopped
         halfway: the colour served to choose, not to recognise while scanning
         the list.

         3 px and on the LEFT, not on top. Going down a list the rules
         line up in a single column and the eye follows it; a horizontal
         stripe on top of each card reads as a separator, and that is already
         forbidden in §8-ter h. It is also the measure the app ALREADY used in
         the chips and the search cards: there are no two grammars.

         ⛔ IT GOES AT THE END OF THE FILE ON PURPOSE. Almost all cards declare
         `border: 1px solid var(--border)` as a shorthand, which includes the
         left border; at equal specificity the last rule in the file wins.
         Written higher up, the rule would not show on any card and no
         error would say so.

         ⚠️ IT TOUCHES NO PADDING: with global `box-sizing: border-box`, the
         rule eats 3 px from inside and the outer width does not change. A
         fixed `padding-left` here would break the lists that do not use
         14 px.

         ⚠️ A bare `.tl-fil` paints the rule in GREY. It goes on ALL the
         cards in a list, typed or not: that way none ends up 3 px narrower
         than its neighbour, which is noticeable even if you do not know why.

         ⛔ WHAT IS NOT OF THAT TYPE DOES NOT CARRY IT. A shooter is not a
         discipline: in the ranking, which is filtered by type, the rows are
         people and go without a rule.
         ═══════════════════════════════════════════════════════════════ */
      .tl-fil        { border-left: 3px solid var(--border); }
      .tl-fil-aire   { border-left-color: var(--cv-type-aire-bd); }
      .tl-fil-fuego  { border-left-color: var(--cv-type-fuego-bd); }
      .tl-fil-aahh   { border-left-color: var(--cv-type-aahh-bd); }
      .tl-fil-plato  { border-left-color: var(--cv-type-plato-bd); }
      .tl-fil-club   { border-left-color: var(--tl-ent-club-bd); }
      .tl-fil-fed    { border-left-color: var(--tl-ent-fed-bd); }
      .tl-fil-campo  { border-left-color: var(--tl-ent-campo-bd); }

