/* ============================================================
   Shared stylesheet for every page of the tournament site.
   ============================================================
   Extracted 2026-09-23. Until then index.html and team.html each carried
   their own copy of this, and the two had ALREADY diverged by ~2KB — the
   signup styles existed on one page and not the other. That is the whole
   argument for this file: a duplicated stylesheet does not stay duplicated,
   it silently becomes two different stylesheets.
   ============================================================ */
/* ============================================================
   TOKENS
   ============================================================ */
:root{
  /* ================================================================
     ALTRUAGENT TOURNAMENT — Direction B, "Observatory"
     ================================================================
     Every colour here is lifted from the tournament logo's own gradient
     stops (references/Tournament Logo.svg), not invented alongside it.

     THE RULE THAT MAKES THIS DIRECTION WORK: colour is rare. The page is
     greyscale by default; crimson appears on the primary call to action and
     nowhere else, and cobalt appears on links, focus rings and the arc
     motif. A brand colour used everywhere stops reading as a brand colour
     and starts reading as a wash — which is what the previous gold did,
     tinting borders, labels, icons, tags and rules alike.
     ================================================================ */

  /* -- surfaces. Blue-tinted near-black, from the poster's base. --------- */
  --canvas:     #0A0C10;
  --elev:       #0E1117;
  --surface:    #12151C;
  --line:       rgba(255,255,255,0.08);
  --line-hover: rgba(255,255,255,0.14);

  /* -- type. All three pass WCAG AA on --canvas: 17.9 / 9.3 / 6.6. ------
     The previous --paper-faint (#6a7282) sat at 2.8-4.1 against this
     background and was used for every mono label on the site, which is why
     twelve elements failed AA. --faint is the fix, not a preference. */
  --text:       #F2F5F9;
  --muted:      #A9B4C4;
  --faint:      #8B97A8;

  /* -- brand. From the logo's gradient stops. ---------------------------
     #C73030 is the one colour that terminates BOTH the dark and the light
     logo variants, so it is the brand red rather than one of its tints.

     It contrasts 3.63:1 on canvas — large text only, never a label. Where
     red must carry small text, --red-text does it at 6.88.

     The two brand colours need OPPOSITE text treatments, which is easy to
     get backwards: white on crimson is 5.39 (passes), black on crimson is
     3.63 (fails); black on cobalt is 5.31 (passes), white on cobalt is
     3.69 (fails). Hence --on-red and --on-blue. */
  --red:        #C73030;
  --red-hover:  #D84040;
  --red-text:   #F0736F;
  --on-red:     #FFFFFF;
  --blue:       #4D7DFF;
  --blue-pale:  #A9C0FF;
  --on-blue:    #0A0C10;
  --navy:       #082B83;

  /* -- type families ----------------------------------------------------
     Instrument Sans for display and body. One family across both, which is
     what GitHub does with Mona Sans: it keeps the page coherent and the
     payload small. Slightly condensed with a tall x-height, so it holds a
     tight headline without the neutrality of Inter — which the brief rules
     out by name.

     IBM Plex Mono for labels, dates and counters. Genuine engineering
     heritage rather than a coding font playing one, and it was already
     loaded here, so it costs nothing. */
  --sans:  'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --serif: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --mono:  'IBM Plex Mono', 'SF Mono', Menlo, monospace;

  /* -- motion. Unchanged: it already matches the brief. ------------------ */
  --ease:      cubic-bezier(0.165, 0.84, 0.44, 1);
  --dur-fast:  .15s;
  --dur:       .22s;
  --dur-slow:  .32s;
}

/* Legacy aliases. The markup and the rest of this stylesheet still name the
   old gold palette in ~65 places; rather than a risky find-and-replace
   across four files, the old names now resolve to the new system. Each maps
   to the NEUTRAL equivalent, not a coloured one — that is what keeps colour
   rare. New rules should use the tokens above. */
:root{
  --black:       var(--canvas);
  --black-elev:  var(--elev);
  --black-card:  var(--surface);
  --paper:       var(--text);
  --paper-dim:   var(--muted);
  --paper-faint: var(--faint);
  --gold:        var(--red);        /* primary action only */
  --gold-bright: var(--text);       /* was an accent everywhere; now neutral */
  --gold-dim:    var(--faint);      /* was tinting borders and labels */
}

*{box-sizing:border-box; margin:0; padding:0;}
html{scroll-behavior:smooth;}
body{
  background:var(--black);
  color:var(--paper);
  font-family:var(--sans);
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
a{color:inherit; text-decoration:none;}
img{max-width:100%; display:block;}
.wrap{max-width:1180px; margin:0 auto; padding:0 32px;}

::selection{background:var(--gold-dim); color:var(--paper);}

/* ============================================================
   TEXTURE — hairline gold grid, used sparingly as backdrop
   ============================================================ */
.grain{
  position:fixed; inset:0; pointer-events:none; z-index:1; opacity:.035; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ============================================================
   NAV
   ============================================================ */
nav{
  position:sticky; top:0; z-index:100;
  background:rgba(10,10,10,0.88); backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line);
}
/* Grid, and the columns are `auto 1fr auto` — NOT `1fr auto 1fr`.
   `1fr auto 1fr` puts the link group on the PAGE centre, which is only the
   right answer while the two flanks are close in width. They are not: the
   wordmark reads "AI Agent Gaming Tournament" and runs ~150px wider than
   the button beside it, so page-centring pushed the links hard up against
   the wordmark (single-digit px) while leaving a ~160px void on the right.
   `auto 1fr auto` centres the links in the space BETWEEN the two blocks,
   which splits that slack evenly and is what actually reads as centred.
   `minmax(0,1fr)` rather than `1fr` so the middle column is allowed to be
   narrower than its content instead of forcing the bar wider. */
nav .wrap{display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center;
  /* A floor under the space either side of the links, so they can never
     sit flush against the wordmark or the button no matter how the copy
     in any of the three changes. */
  column-gap:clamp(28px, 3.4vw, 56px);
  min-height:76px; padding-top:14px; padding-bottom:14px;}
nav .nav-right{justify-self:end;}
.brand{display:flex; align-items:center; white-space:nowrap;}
.brand-mark{
  width:34px; height:34px; margin-right:12px; flex-shrink:0;
  border:1.5px solid var(--gold); border-radius:2px;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--serif); font-weight:600; font-size:16px; color:var(--gold-bright);
  background:linear-gradient(160deg, rgba(163,128,58,0.12), transparent);
}
.brand-text{font-family:var(--serif); font-weight:600; font-size:18px; letter-spacing:-0.01em; color:var(--paper);}

/* Centred in the space between the wordmark and the right-hand button,
   rather than starting flush against the wordmark. `flex:1` already
   made this element span that gap; it was just packing its children to
   the start. The last link's margin-right is zeroed so the group's
   optical centre is not pulled 24px left by trailing whitespace. */
.nav-links{display:flex; justify-content:center; gap:clamp(16px, 1.7vw, 28px); font-family:var(--mono); font-size:12px;
  letter-spacing:.04em; color:var(--muted); flex:1;}
/* Spacing lives on the container as `gap`, not as margin-right on each
   link. Margin leaves trailing space after the last one, which pulls the
   group's optical centre off by half the gap — and :last-of-type cannot
   correct it, because the last <a> here is the mobile CTA, hidden at
   desktop. 24px rather than 32 so eight links clear the 1000px breakpoint
   without wrapping. */
.nav-links a{transition:color var(--dur-fast) var(--ease); white-space:nowrap;}
.nav-links a:hover{color:var(--text);}
.nav-links .nav-cta{display:none;}

.nav-right{display:flex; align-items:center; gap:12px;}
.nav-toggle{
  display:none; width:38px; height:38px; flex-shrink:0; align-items:center; justify-content:center;
  background:none; border:1px solid var(--line); border-radius:4px; color:var(--paper); cursor:pointer;
}
.nav-toggle svg{width:18px; height:18px;}

.btn{
  display:inline-flex; align-items:center; white-space:nowrap;
  padding:11px 22px; border-radius:2px; font-family:var(--sans); font-size:13.5px; font-weight:700;
  letter-spacing:.01em; border:1px solid transparent; cursor:pointer;
  transition:background-color var(--dur) var(--ease), border-color var(--dur) var(--ease),
             color var(--dur) var(--ease), transform var(--dur-fast) var(--ease);
}
/* The one crimson element on the page. Everything else is greyscale, so
   this reads as THE action rather than as another accent. */
.btn-gold{background:var(--red); color:var(--on-red); border-color:var(--red);}
.btn-gold:hover{background:var(--red-hover); border-color:var(--red-hover); transform:translateY(-1px);}
/* `background:transparent` is load-bearing, not tidiness: an <a> has no
   default background but a <button> does (UA ButtonFace, #efefef), so
   without this a .btn-line <button> renders near-white text on a
   near-white field. Found on the simple page's signup, 2026-09-23. */
.btn-line{border-color:var(--line); color:var(--text); background:transparent;}
.btn-line:hover{border-color:var(--line-hover); color:var(--text);}

/* ============================================================
   HERO — constellation motif
   ============================================================ */
.hero{position:relative; padding:110px 0 90px; overflow:hidden;}
/* ============================================================
   HERO FIELD — the poster, static
   ============================================================
   Four masses as stacked radial gradients on one element. An animated
   version existed briefly (six drifting layers, pointer parallax, grain)
   and was reverted: the movement pulled attention to the background at the
   exact moment the page is trying to say what the event is, and the
   saturation needed to make drifting clouds read at all had to be fought
   back with a scrim over the text column. Still is better here.

   COLOURS ARE THE POSTER'S, SAMPLED RATHER THAN EYEBALLED. Taking the most
   saturated pixel per region of AI_Agent_Poster.jpg returns #640F1B and
   #6C2630 (hue ~351) and #185CFF, #0D3C7F, #6092FF (hue 215-222). Nothing
   between the two. The violet that reads across the poster's top edge is
   where its red and blue masses overlap, not a colour it contains — so
   there is no violet layer here either; the top-left mass is the poster's
   periwinkle BLUE, and any violet comes from the overlap.

   Percentage positions and sizes, so the composition reflows with the
   viewport rather than drifting off a phone. Soft-edged by each gradient's
   own falloff — no filter:blur(), which would force the whole stack onto
   its own compositor layer and cost more than it buys.

   CONTRAST IS MEASURED, NOT ASSUMED. These sit behind live text; the alphas
   below are the highest that keep the headline and the small mono labels
   above WCAG AA over the field's brightest point.
   ============================================================ */
.hero-glow{
  position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden;
  background:
    /* Periwinkle blue, top left — the poster's #6092FF. */
    radial-gradient(58% 52% at 10% 4%,   rgba(96,146,255,.15), transparent 68%),
    /* Crimson sphere, top right — the dominant mass, the logo's #C73030. */
    radial-gradient(52% 58% at 80% -4%,  rgba(199,48,48,.32),  transparent 66%),
    /* Cobalt, lower left — the poster's #185CFF. */
    radial-gradient(46% 42% at 26% 86%,  rgba(24,92,255,.20),  transparent 70%),
    /* Deep navy body rising from the bottom edge — #0D3C7F. */
    radial-gradient(72% 66% at 56% 108%, rgba(13,60,127,.50),  transparent 72%);
}
/* A tighter pair of highlights. The poster's masses have a discernible
   centre rather than dissolving evenly, and that is what stops them reading
   as a generic gradient wash. */
.hero-glow::after{
  content:''; position:absolute; inset:0;
  background:
    radial-gradient(28% 30% at 82% 6%,  rgba(214,81,81,.10),  transparent 60%),
    radial-gradient(34% 28% at 52% 98%, rgba(169,192,255,.09), transparent 60%);
}

.hero-content{position:relative; z-index:2;}
.hero-text{max-width:760px;}
.eyebrow{
  display:inline-flex; align-items:center; font-family:var(--mono); font-size:11.5px;
  letter-spacing:.14em; text-transform:uppercase; color:var(--gold-bright);
  border:1px solid var(--gold-dim); padding:8px 16px; border-radius:100px; margin-bottom:34px;
}
.eyebrow .dot{width:5px; height:5px; border-radius:50%; background:var(--red); margin-right:10px;}

h1{
  font-family:var(--serif); font-weight:600; font-size:76px; line-height:.98; letter-spacing:-0.02em;
  max-width:820px; margin-bottom:30px;
}
h1 em{font-style:italic; font-weight:500; color:var(--gold-bright);}

.hero-sub{font-size:19px; color:var(--paper-dim); max-width:600px; margin-bottom:44px; font-weight:400;}
.hero-actions{display:flex; flex-wrap:wrap;}
.hero-actions a{margin-right:16px; margin-bottom:12px; padding:15px 30px; font-size:14.5px;}

/* Event facts — plain line, no boxes */
.event-line{
  font-family:var(--mono); font-size:13px; letter-spacing:.02em; color:var(--paper-dim);
  margin-top:44px; padding-top:24px;
}
.event-line strong{color:var(--paper); font-weight:600;}
.event-line .sep{color:var(--gold-dim); margin:0 10px;}
.event-note{font-family:var(--mono); font-size:11px; color:var(--paper-faint); margin-top:10px;}

/* ============================================================
   COUNTDOWN — hero urgency driver
   Hidden until JS fills it, so a no-JS visitor sees no empty frame
   rather than a row of dashes.
   ============================================================ */
.countdown{margin-top:44px; padding-top:26px;}
.countdown-label{
  font-family:var(--mono); font-size:11px; letter-spacing:.16em; text-transform:uppercase;
  color:var(--gold-dim); margin-bottom:16px;
}
.countdown-units{display:flex; flex-wrap:wrap; gap:14px;}
.cd-unit{
  min-width:78px; padding:12px 16px 10px;
  border:1px solid var(--line); border-radius:3px;
  background:linear-gradient(160deg, rgba(163,128,58,0.07), transparent);
}
.cd-num{
  display:block; font-family:var(--mono); font-weight:500; font-size:30px; line-height:1.05;
  color:var(--paper);
  /* Fixed-width digits: without this every tick of the seconds column
     nudges the row's width and the whole hero shivers once a second. */
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
}
.cd-cap{
  display:block; margin-top:6px;
  font-family:var(--mono); font-size:9.5px; letter-spacing:.14em; text-transform:uppercase;
  color:var(--paper-faint);
}
/* Terminal state — the widget says the tournament is happening rather than
   counting into negative numbers or silently vanishing. */
.countdown-live{
  font-family:var(--mono); font-size:15px; letter-spacing:.04em;
  color:var(--gold-bright);
}
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
@media (max-width:480px){
  .cd-unit{min-width:0; flex:1 1 calc(50% - 7px); padding:10px 12px 8px;}
  .cd-num{font-size:24px;}
}

/* ============================================================
   PROGRAM LABEL SYSTEM (section markers, like an event program)
   ============================================================ */
/* No border-top. The hairline between every section was doing the job
   that whitespace already does, and on a page this dark it read as a
   seam rather than a division. Rhythm now comes from padding alone. */
section{padding:100px 0; position:relative;}
.program-tag{
  font-family:var(--mono); font-size:11.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--gold);
  display:flex; align-items:center; margin-bottom:20px;
}
.program-tag::before{content:''; width:24px; height:1px; background:var(--gold-dim); margin-right:12px;}
/* The cap moved off the heading and onto the paragraph. A 680px box was
   wrapping every h2 to two lines while the line length it was there to
   protect is the SUB-head's, which still has it. Headings wrap only
   when the viewport actually makes them, which on mobile they must. The
   sub-head already caps itself at 560px further down. */
.section-head{margin-bottom:56px;}
h2{font-family:var(--serif); font-weight:600; font-size:42px; letter-spacing:-0.015em; margin-bottom:16px; line-height:1.08;}
.section-sub{color:var(--paper-dim); font-size:17px; max-width:560px;}

/* ============================================================
   ABOUT / ORIGIN
   ============================================================ */
/* ============================================================
   ABOUT — three paragraphs, given a reading hierarchy
   ============================================================
   With the stat column gone this is a single block of prose, and a wall of
   three equal paragraphs at full width is the hardest thing on the page to
   start reading.

   Two changes do most of the work:

   MEASURE. Capped in rem, not ch. `ch` is the width of the "0" glyph, and
   in Instrument Sans that is ~40% wider than an average lowercase letter —
   80ch measured out at 113 actual characters a line, not the ~65 it looks
   like it should be. Measured with canvas text metrics rather than
   estimated: 34rem gives ~68 characters and 38rem gives ~60 on the larger
   lede, both inside the comfortable 60-75 band. It ran at 113 before.

   A LEDE. The first paragraph is set larger and lighter in colour, so the
   section has somewhere to begin instead of three identical greys. This is
   typographic hierarchy rather than new content — no copy was changed.
   ============================================================ */
.about-grid{display:block;}
.about-body{
  font-size:17px; color:var(--muted); line-height:1.8;
  /* TWO COLUMNS, not one narrow block hugging the left edge.
     The tension here is real: prose spanning a 1100px container runs to
     ~113 characters a line, which is roughly 50% past the point the eye
     reliably finds the next line. Columns resolve it — the block uses the
     full page width while each line stays around 65 characters.
     Measured, not estimated, with canvas text metrics.

     A GRID, NOT `columns:2`. CSS multi-column balances the two columns by
     height, so which paragraph lands where is the browser's decision, not
     the author's. The copy is written to be read left column first, so the
     split is declared in the markup instead: one .about-col per column. */
  display:grid; grid-template-columns:1fr 1fr; gap:64px; align-items:start;
}
.about-body strong{color:var(--text); font-weight:600;}
.about-body p + p{margin-top:26px;}

@media (max-width:860px){
  /* One column below tablet: at phone widths a second column leaves ~20
     characters a line, which is worse than a long measure.

     The max-width matters as much as the column count — dropping to one
     column at 860px without it put the prose back at 100 characters a
     line, which is the problem the columns were introduced to solve.
     34rem holds it near 68. Below ~600px the container is already narrower
     than this, so it stops applying on its own. */
  .about-body{grid-template-columns:1fr; gap:26px; max-width:34rem;}
}

/* ============================================================
   THE GAMES
   ============================================================ */
/* Grid, not flex-wrap. A wrapped flex row sizes itself independently of the
   row above it, so the four cards came out as two pairs of two different
   heights. `grid-auto-rows:1fr` makes every row share one height, and
   `auto-fit` keeps the 2-up / 1-up reflow that flex-wrap was providing. */
.game-row{
  display:grid; grid-template-columns:repeat(2, 1fr);
  gap:24px; grid-auto-rows:1fr;
}
/* Explicitly two columns, not auto-fit. auto-fit with a 280px floor fitted
   THREE across at desktop and left the fourth game alone on its own row with
   a gap beside it — four cards want 2x2, and that is a layout decision
   rather than something to derive from a minimum width. */
@media (max-width:760px){
  .game-row{grid-template-columns:1fr;}
}
.game-card{
  background:var(--surface); border:1px solid var(--line);
  padding:36px; position:relative;
  /* Column flex so the capabilities block can be pushed to the bottom edge;
     without it the cards match on the outside while their internals sit at
     different heights, which is the part the eye actually reads. */
  display:flex; flex-direction:column;
  transition:border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
/* Border firms up rather than a shadow appearing — the brief asks for crisp
   thin borders and subtle surfaces instead of heavy shadows. */
.game-card:hover{border-color:var(--line-hover); transform:translateY(-2px);}
.game-index-row{display:flex; align-items:center; justify-content:space-between; margin-bottom:18px;}
.game-index{font-family:var(--mono); font-size:11px; color:var(--gold-dim); letter-spacing:.1em;}
.game-icon{width:26px; height:26px; color:var(--gold-bright); opacity:.9; flex-shrink:0;}
.game-card h3{font-family:var(--serif); font-size:26px; font-weight:600; margin-bottom:12px;}
.game-card p{color:var(--paper-dim); font-size:15px; line-height:1.75;}
/* The challenge question. Set in the card's text colour rather than the
   muted grey of the description above it, so it reads as the card's direct
   address to the reader and not as a third sentence of body copy. It sits
   immediately under the paragraph; the capabilities block below keeps its
   `margin-top:auto`, so the question travels with the prose and the block
   stays pinned to the card's bottom edge. */
.game-card p.game-challenge{
  color:var(--text); font-size:15px; line-height:1.6; font-weight:500;
  /* margin-bottom, not just the divider's own padding. `margin-top:auto` on
     .game-tests can only ever add space, so on the two cards whose question
     is long enough to fill the card it collapsed to zero and the question
     sat flush on the rule below it. This is the floor under that gap. */
  margin-top:16px; margin-bottom:22px;
}
.game-tests{margin-top:auto; padding-top:20px; border-top:1px solid var(--line);}
.game-tests-label{font-family:var(--mono); font-size:10px; letter-spacing:.1em; text-transform:uppercase; color:var(--paper-faint); margin-bottom:10px;}
.tag-row{display:flex; flex-wrap:wrap;}
/* Sized so the longest capability set — Red Alert's "Resource management /
   Real-time tactics / Long-horizon planning" — fits on ONE row at the card's
   width. When that set wrapped to two rows, its block grew taller than its
   neighbour's, and since the block is bottom-aligned the divider above it
   rode up against the paragraph on one card while leaving a gap on the
   other. The uneven spacing was the symptom; the wrap was the cause. */
.tag{
  font-family:var(--mono); font-size:10.5px; color:var(--muted);
  border:1px solid var(--line); border-radius:100px;
  padding:4px 9px; margin-right:6px; margin-bottom:8px; white-space:nowrap;
}

/* ============================================================
   SCHEDULE
   ============================================================
   Two blocks, morning and afternoon, side by side at desktop and stacked
   below 760px — the same break the game grid uses. Rows are a fixed time
   column plus the item, so the times line up down each block and the eye
   can scan them without reading the descriptions. The two game sessions
   and the winners get the text colour; everything else stays muted, which
   is the same "colour is rare" rule the tokens describe. */
/* The section-sub cap is 560px; this line is meant to run as one line at
   desktop, so it drops the cap. It still wraps on narrow screens. */
.sched-sub{max-width:none;}
.sched-row{display:grid; grid-template-columns:repeat(2, 1fr); gap:24px; align-items:start;}
@media (max-width:760px){ .sched-row{grid-template-columns:1fr;} }
.sched-block{background:var(--surface); border:1px solid var(--line); padding:32px 36px;}
.sched-label{
  font-family:var(--mono); font-size:11px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--faint); margin-bottom:12px;
}
.sched{list-style:none;}
.sched li{
  display:grid; grid-template-columns:7.5rem 1fr; gap:16px;
  padding:14px 0; border-top:1px solid var(--line);
  font-size:15px; line-height:1.55; color:var(--muted);
}
.sched li:first-child{border-top:none;}
.sched time{font-family:var(--mono); font-size:13px; color:var(--faint); padding-top:2px; white-space:nowrap;}
.sched .tag-row{margin-top:10px;}
.sched li.sched-key, .sched li.sched-win{color:var(--text); font-weight:500;}
.sched .tag{font-weight:400;}
.sched-key time, .sched-win time{color:var(--text);}
.sched-break span{font-style:italic;}
@media (max-width:480px){
  .sched-block{padding:24px 20px; min-width:0;}
  .sched li{grid-template-columns:6.25rem 1fr; gap:12px;}
}

/* ============================================================
   DEMOS
   ============================================================ */
.demo-row{display:flex; flex-wrap:wrap;}
.demo-card{
  flex:1 1 30%; min-width:280px; background:var(--black-card); border:1px solid var(--line);
  margin-right:24px; margin-bottom:24px; overflow:hidden;
}
.demo-card:last-child{margin-right:0;}
.demo-card video{width:100%; aspect-ratio:16/9; background:#000; display:block;}
.demo-meta{padding:20px 22px;}
.demo-title{font-family:var(--serif); font-size:19px; font-weight:600; margin-bottom:6px;}
.demo-desc{color:var(--paper-dim); font-size:14px; line-height:1.6;}

/* ============================================================
   MODES — ticket stubs
   ============================================================ */
.ticket-row{display:flex; flex-wrap:wrap;}
.ticket{
  flex:1 1 45%; min-width:300px; margin-right:28px; margin-bottom:28px;
  background:var(--black-card); border:1px solid var(--line); border-radius:6px;
  position:relative; transition:border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.ticket:hover{border-color:var(--gold-dim); transform:translateY(-2px);}
.ticket:last-child{margin-right:0;}
.ticket-main{padding:34px;}
.ticket-tag{
  font-family:var(--mono); font-size:11px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--gold-bright); border:1px solid var(--gold-dim); padding:5px 12px; border-radius:100px;
  display:inline-block; margin-bottom:20px;
}
.ticket-main h3{font-family:var(--serif); font-size:25px; font-weight:600; margin-bottom:10px;}
.ticket-main p{color:var(--paper-dim); font-size:14.5px; margin-bottom:18px;}
.ticket-list{list-style:none; font-size:14px; color:var(--paper-dim);}
.ticket-list li{padding-left:20px; position:relative; margin-bottom:9px;}
.ticket-list li::before{content:'—'; position:absolute; left:0; color:var(--gold);}

.chip-row{display:flex; flex-wrap:wrap; gap:10px; margin-top:18px;}
.chip-pending{
  display:inline-flex; align-items:center; gap:7px;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.03em; color:var(--paper-faint);
  border:1px solid var(--line); border-radius:100px; padding:6px 13px 6px 10px;
}
.chip-pending svg{width:15px; height:15px; color:var(--gold-dim); flex-shrink:0;}

.foot-social{display:flex; align-items:center; gap:10px;}
.foot-social-icon{
  width:30px; height:30px; border:1px solid var(--line); border-radius:50%;
  display:flex; align-items:center; justify-content:center; color:var(--gold-dim);
  transition:border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.foot-social-icon svg{width:15px; height:15px;}
.foot-social-icon:hover{border-color:var(--gold-dim); color:var(--gold-bright);}

/* ============================================================
   SPONSORS
   ============================================================ */
/* `gap` rather than margin-right plus a :last-child override. The margin
   approach lines up in a single row but breaks the moment the row wraps:
   the last card keeps its zeroed margin while the others do not, so stacked
   cards come out 20px wider than their neighbours. Surfaced when Replit
   made it three. */
.sponsor-row{display:flex; flex-wrap:wrap; gap:20px;}
/* A 2x2 GRID from four sponsors on. Flex with a 300px floor fit three
   across the 1116px content box but not four (4x300 + 3x20 = 1260), so
   Matcherino wrapped onto a row of its own and `flex:1` stretched it the
   full width. Two by two keeps the four cards equal at every width above
   phone size, where they drop to one column. The three-sponsor row on
   sponsors.html keeps the flex rule. */
.sponsor-row:has(> .sponsor-card:nth-child(4)){
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); grid-auto-rows:1fr;
}
.sponsor-row:has(> .sponsor-card:nth-child(4)) .sponsor-card{min-width:0;}
@media (max-width:620px){
  .sponsor-row:has(> .sponsor-card:nth-child(4)){grid-template-columns:minmax(0,1fr);}
}
/* STACKED, not side by side. The badge sat to the LEFT of the name, which
   works while every logo is a small square mark. The Amazon Science Hub
   artwork is a 244x72 lockup, and at a size where its second line is
   actually readable it takes ~163px of a ~300px card — leaving the name
   to wrap to four lines beside it and dragging all three cards to 170px
   tall. Stacked, the logo gets the card's full width and the name gets
   its own line under it. */
.sponsor-card{
  flex:1; min-width:300px; background:var(--black-card); border:1px solid var(--line);
  padding:28px; display:flex; flex-direction:column; align-items:flex-start; gap:18px;
}
.sponsor-badge{
  width:64px; height:64px; border:1px solid var(--gold-dim); flex-shrink:0;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--mono); font-weight:600; font-size:13px; color:var(--gold-bright);
  /* Padding so a logo does not touch the hairline frame. */
  padding:8px;
}
/* A real logo replaces the lettermark. The <img> carries
   onerror="this.remove()", so until the file is dropped into
   assets/logos/ the badge falls back to the initials rather than showing a
   broken-image glyph — and it upgrades itself the moment the file exists,
   with no markup change.

   object-fit:contain keeps non-square marks (Oracle's is very wide) from
   being stretched to fill a square frame. */
.sponsor-logo{width:100%; height:100%; object-fit:contain; display:block;}
/* The badge is the logo and nothing else: no border once artwork is in it
   (a box drawn around someone's logo reads as part of the logo) and no
   lettermark. `onerror` still strips a missing image, which leaves an empty
   frame rather than a broken-image glyph. */
.sponsor-card:has(.sponsor-logo) .sponsor-badge{border-color:transparent; padding:2px;}
/* Names WRAP. This was `white-space:nowrap`, so that one long name could
   not go to two lines while its neighbours sat on one and leave the cards
   at different heights. That reason is gone: .sponsor-row is a flex row at
   the default `align-items:stretch`, so a taller card pulls the others up
   with it and they still agree — measured at 1440 and 1100 with all three
   cards equal.

   What nowrap does instead is overflow. With three cards each gets ~332px
   at 1100px wide, and neither "Oracle Cloud Infrastructure" nor "Amazon
   Science Hub at UCLA" fits beside its badge in what is left; nowrap does
   not shrink text, it pushes it past the card border. */
/* ---- A WIDE LOCKUP ON A LIGHT PLATE -------------------------------
   The Amazon Science Hub artwork is not a square mark like the others. It
   is a 244x72 lockup, and it is supplied as dark ink on transparency:
   UCLA blue #0A5688 and Amazon #232F3E, mean luminance 77 of 255. Dropped
   into the 50px badge as-is it would be both illegible at ~50x15 and all
   but invisible against --surface.

   So this badge gets its own geometry and a white plate behind it. The
   plate is the honest fix: the alternative is filtering or inverting
   someone's logo, which changes their colours. A logo sitting on white is
   a normal thing on a dark page; a recoloured one is not.

   The rule below it strips borders and padding from any badge holding a
   logo, so this one restates its padding to win on order. */
/* Height is set so the ARTWORK matches the square marks, not so the boxes
   do: the plate carries 11px of padding the others do not, so an equal box
   height would leave this logo visibly smaller than its neighbours. 80px
   box minus that padding puts the lockup at 58px tall — the same as the
   Oracle and Replit marks — and 197px wide, inside the ~276px the narrowest
   three-across card has to give it. */
.sponsor-badge-plate{
  width:auto; height:80px; padding:11px 14px;
  /* Off-white rather than pure #FFF, and a softer radius: at full white
     and 3px the plate read as a patch cut into the card. --text is the
     page's own near-white, so the plate belongs to this palette instead of
     introducing a brighter one. */
  background:#F2F5F9; border:none; border-radius:10px;
}
.sponsor-badge-plate .sponsor-logo{width:auto; height:100%;}
.sponsor-card:has(.sponsor-badge-plate) .sponsor-badge{padding:11px 14px;}
/* This one name wraps. .sponsor-name is nowrap so the cards agree on their
   own height, but "Amazon Science Hub at UCLA" beside a 146px plate needs
   ~215px in the ~139px a third of the row leaves it, and nowrap does not
   shrink — it overflows the card. Wrapping costs nothing here: the row is
   `flex-wrap` with the default `align-items:stretch`, so the other cards
   grow to match rather than being left short. */

.sponsor-name{font-family:var(--serif); font-weight:600; font-size:17px; margin-bottom:3px;}
.sponsor-role{font-family:var(--mono); font-size:11px; letter-spacing:.05em; text-transform:uppercase; color:var(--paper-faint);}

/* ============================================================
   PROGRAM / COMMITTEE
   ============================================================ */
.committee-group{margin-bottom:52px;}
.committee-group:last-child{margin-bottom:0;}
.committee-head{display:flex; align-items:baseline; flex-wrap:wrap; margin-bottom:22px; padding-bottom:14px; border-bottom:1px solid var(--line);}
.committee-title{font-family:var(--serif); font-size:20px; font-weight:600; margin-right:16px;}
.committee-chair{font-family:var(--mono); font-size:11.5px; color:var(--paper-faint); letter-spacing:.03em;}
.committee-chair strong{color:var(--gold-bright); font-weight:500;}

.member-row{display:flex; flex-wrap:wrap;}
.member{
  flex:1 1 30%; min-width:230px;
  padding:16px 0; margin-right:24px; border-bottom:1px solid var(--line);
}
.member-link{display:block; width:100%; transition:opacity var(--dur) var(--ease), transform var(--dur) var(--ease);}
a.member-link:hover{opacity:.75; transform:translateX(2px);}
.member-name{font-size:14.5px; font-weight:600; color:var(--paper); display:flex; align-items:center;}
/* The LinkedIn mark, and — for a member whose link is their own page
   rather than a LinkedIn profile — a plain external-link glyph. Same size
   and weight, so a row of mixed links still reads as one list. Borrowing
   the LinkedIn mark for a faculty page would misstate where the link
   goes. */
.member-name .li,
.member-name .ext{
  margin-left:7px; width:14px; height:14px; opacity:.55; flex-shrink:0;
}
.member-name .ext{width:12px; height:12px;}
.member-role{font-family:var(--mono); font-size:11px; color:var(--paper-faint); margin-top:2px;}

/* ============================================================
   CTA BAND
   ============================================================ */
.cta-band{text-align:center; padding:110px 0;}
.cta-band h2{font-size:46px; margin-bottom:18px;}
.cta-band .section-sub{max-width:520px; margin:0 auto 40px;}
.cta-band .hero-actions{justify-content:center; display:flex; flex-wrap:wrap;}

/* ============================================================
   FOOTER
   ============================================================ */
footer{padding:44px 0;}
footer .wrap{display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap;}
.foot-brand{display:flex; align-items:center;}
.foot-meta{font-family:var(--mono); font-size:11.5px; color:var(--paper-faint); letter-spacing:.02em;}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:860px){
  h1{font-size:44px;}
  h2{font-size:30px;}
  .game-card{flex:1 1 100%; margin-right:0;}
  .demo-card{flex:1 1 100%; margin-right:0;}
  .ticket{flex:1 1 100%; margin-right:0;}
  .member{flex:1 1 100%; margin-right:0;}
  .about-body{margin-right:0; flex:1 1 100%;}

  nav .wrap{flex-wrap:nowrap;}
  .nav-links{
    display:none; position:absolute; top:100%; left:0; right:0;
    flex-direction:column; background:var(--black-elev); border-bottom:1px solid var(--line);
    padding:4px 32px 16px; max-height:calc(100vh - 76px); overflow-y:auto;
  }
  .nav-links.open{display:flex;}
  .nav-links{gap:0;}
  .nav-links a{padding:13px 0; border-bottom:1px solid var(--line);}
  .nav-links .nav-cta{display:block; margin-top:14px; text-align:center; padding:12px; border-bottom:none;}
  .nav-toggle{display:flex;}
  /* Any button, not just the gold one. This was written as .btn-gold and
     silently stopped matching when the nav CTA became .btn-line, which
     pushed the menu toggle 6px off-screen and gave the whole page a
     horizontal scrollbar on mobile. */
  .nav-right .btn{display:none;}
  .brand{margin-right:0;}
}

@media (max-width:480px){
  .wrap{padding:0 20px;}
  h1{font-size:34px;}
  .game-card, .demo-card, .ticket, .member, .about-body{min-width:0;}
  .brand-sub{display:none;}
  .brand-text{font-size:15px;}
}

/* ============================================================
   SCROLL REVEAL
   ============================================================
   The `.reveal` class is applied BY SCRIPT, never in the markup. That
   ordering matters: if the JS fails or is blocked, nothing ever gets
   `opacity:0` and the page is simply static rather than permanently blank.
   Hiding content in CSS and relying on JS to show it is how a page ends up
   invisible for the people least able to debug it.

   Transform is a few pixels only. A long travel distance reads as a slide
   show; this reads as the page settling.
   ============================================================ */
.reveal{
  opacity:0; transform:translateY(14px);
  transition:opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
}
.reveal.is-in{opacity:1; transform:none;}

/* ============================================================
   REDUCED MOTION
   ============================================================
   Replaces the previous block, which covered .game-card alone. Anyone who
   has asked their OS to reduce motion gets the finished state instantly —
   content still appears, it just does not travel. The script checks the
   same query and skips observing entirely.
   ============================================================ */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
  .reveal{opacity:1; transform:none;}
}

/* ============================================================
   FORM PRIMITIVES — shared by every form on the site
   ============================================================
   These live on their own rather than inside any one form's block. They
   were previously nested in the homepage signup styles and disappeared with
   it when that band was replaced, which left the honeypot VISIBLE on the
   page — a field labelled "Leave this empty" sitting in the middle of the
   hero. Anything used by more than one form belongs here.
   ============================================================ */

/* The honeypot. Positioned off-screen rather than display:none — some bots
   skip anything display:none, and this field exists to be filled in. It
   must never be reachable by keyboard either, hence tabindex="-1" in the
   markup. */
.hp-field{position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden;}

.form-msg{font-family:var(--mono); font-size:12px; margin-top:2px; min-height:1em;}
.form-msg.is-error{color:#e2765c;}
.form-msg.is-ok{color:var(--gold-bright);}

/* ============================================================
   EMAIL CAPTURE — sits directly under the hero
   ============================================================
   One field and an arrow. No heading, no supporting copy: placed this high
   the ask has to be readable in a glance, and a paragraph explaining an
   email box is a paragraph nobody reads.
   ============================================================ */
/* No background of its own: the band sat on --black-elev, which made it
   a distinct panel floating in the page. On a site that just had every
   section divider removed, one raised strip is the only seam left. */
.capture-band{background:transparent;}
.capture{display:flex; align-items:center; gap:18px; padding:30px 0; flex-wrap:wrap;}
.capture-form{display:flex; align-items:stretch; gap:0; flex:1 1 420px; max-width:520px; position:relative;}
.capture-input{
  flex:1 1 auto; min-width:0;
  background:var(--black); border:1px solid var(--line); border-right:none;
  border-radius:2px 0 0 2px;
  padding:15px 18px; color:var(--paper); font-family:var(--sans); font-size:14.5px;
  transition:border-color var(--dur) var(--ease);
}
.capture-input::placeholder{color:var(--paper-faint);}
.capture-input:focus{outline:none; border-color:var(--gold-dim);}
.capture-input:focus-visible{outline:2px solid var(--gold); outline-offset:2px; position:relative; z-index:1;}
/* The arrow is the whole button. It carries an aria-label because an arrow
   glyph alone tells a screen reader nothing about what the control does. */
.capture-send{
  flex:0 0 auto; width:54px; border:1px solid var(--red); border-radius:0 2px 2px 0;
  background:var(--red); color:var(--on-red); cursor:pointer;
  display:flex; align-items:center; justify-content:center;
  transition:background-color var(--dur) var(--ease);
}
.capture-send:hover{background:var(--red-hover); border-color:var(--red-hover);}
.capture-send:focus-visible{outline:2px solid var(--gold); outline-offset:2px;}
.capture-send svg{width:19px; height:19px; display:block;}
.capture-send[disabled]{opacity:.55; cursor:not-allowed;}
.capture .form-msg{flex:1 1 100%; margin-top:0;}

/* ============================================================
   ASK — two columns: the question on the left, the fields on the right
   ============================================================ */
.ask{display:flex; flex-wrap:wrap; gap:44px; align-items:flex-start;}
.ask-copy{flex:1 1 320px;}
.ask-copy h2{font-family:var(--serif); font-size:30px; font-weight:600; line-height:1.15; max-width:12ch;}
.ask-form{flex:1 1 380px; min-width:0;}
.ask-form .field{margin-bottom:16px;}
.ask-form .form-actions{margin-top:20px;}
@media (max-width:760px){
  .ask{gap:26px;}
  .ask-copy h2{font-size:25px; max-width:none;}
}

/* ============================================================
   FORMS — the fuller ones on sponsors.html and get-involved.html
   ============================================================
   The homepage band asks for an email and nothing else. These ask properly,
   because by the time someone is on one of these pages they have already
   self-selected and the extra fields cost far less.
   ============================================================ */
.form-card{
  background:var(--black-card); border:1px solid var(--line);
  padding:36px; margin-top:32px;
}
.form-card h3{font-family:var(--serif); font-size:21px; font-weight:600; margin-bottom:6px;}
.form-card > p{font-size:14px; color:var(--paper-dim); margin-bottom:26px; max-width:520px;}

.field-row{display:flex; flex-wrap:wrap; gap:18px; margin-bottom:18px;}
.field{flex:1 1 220px; min-width:0; display:flex; flex-direction:column;}
.field--wide{flex:1 1 100%;}
.field label{
  font-family:var(--mono); font-size:10.5px; letter-spacing:.13em; text-transform:uppercase;
  color:var(--paper-faint); margin-bottom:8px;
}
/* The asterisk is decorative; `required` on the input is what actually
   conveys this to assistive tech. */
.field label .req{color:var(--gold-dim); margin-left:4px;}
.field input, .field select, .field textarea{
  background:var(--black); border:1px solid var(--line); border-radius:2px;
  padding:13px 15px; color:var(--paper); font-family:var(--sans); font-size:14px;
  transition:border-color var(--dur) var(--ease); width:100%;
}
.field textarea{resize:vertical; min-height:110px; font-family:var(--sans); line-height:1.6;}
.field input::placeholder, .field textarea::placeholder{color:var(--paper-faint);}
.field input:focus, .field select:focus, .field textarea:focus{outline:none; border-color:var(--gold-dim);}
.field input:focus-visible, .field select:focus-visible, .field textarea:focus-visible{
  outline:2px solid var(--gold); outline-offset:2px;
}

.check-row{display:flex; flex-wrap:wrap; gap:10px; margin-top:2px;}
/* The whole pill is the hit target, not just the 13px box inside it. */
.check{
  display:inline-flex; align-items:center; gap:9px; cursor:pointer;
  border:1px solid var(--line); border-radius:100px; padding:9px 16px;
  font-size:13.5px; color:var(--paper-dim);
  transition:border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.check:hover{border-color:var(--gold-dim); color:var(--paper);}
.check input{width:13px; height:13px; accent-color:var(--gold); flex-shrink:0;}
.check:has(input:checked){border-color:var(--gold-dim); color:var(--paper);}
.check:has(input:focus-visible){outline:2px solid var(--gold); outline-offset:2px;}

.form-actions{display:flex; flex-wrap:wrap; align-items:center; gap:16px; margin-top:26px;}
.form-actions .btn[disabled]{opacity:.55; cursor:not-allowed; transform:none;}

/* Sub-page header: the hero's job without the hero's height. */
.page-head{padding:78px 0 10px;}
.page-head h1{font-size:46px; line-height:1.05; margin-bottom:18px; max-width:760px;}
.page-head p{font-size:17px; color:var(--paper-dim); max-width:580px;}
@media (max-width:860px){ .page-head h1{font-size:34px;} }

/* ============================================================
   FOCUS — cobalt, site-wide
   ============================================================
   The old stylesheet gave focus rings to form fields only, so keyboard
   users lost the cursor the moment they tabbed onto a link or a button.
   :focus-visible rather than :focus, so a mouse click does not draw one.
   ============================================================ */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--blue); outline-offset:3px; border-radius:2px;
}
::selection{background:var(--navy); color:var(--text);}

/* ============================================================
   HERO — rebuilt for Direction B
   ============================================================
   Order is headline -> subtext -> date/venue -> countdown -> email. Five
   elements where there were seven; the two path buttons moved to the
   Attend cards, which already describe exactly those two paths.

   Sized with clamp() rather than a desktop value plus overrides, so the
   headline is designed at every width instead of shrunk at three of them.
   ============================================================ */
h1{
  /* Back to 88px. The cap was lowered to 76 because "Tournament of Its
     Kind" measured 873px against a 760px column and spilled "Kind" onto a
     line of its own. The headline now breaks before "Its Kind" on purpose,
     so the longest line is "AI Agent Gaming" at 659px — well inside the
     column, and the size no longer has to compensate for a wrap. */
  font-size:clamp(40px, 7.2vw, 88px);
  line-height:.96;
  /* Tight, GitHub-style. Measured on github.com: 64px set at -2.24px,
     which is -0.035em. */
  letter-spacing:-0.035em;
  font-weight:600;
  max-width:16ch;
  margin-bottom:26px;
}
/* No colour on the headline. The poster sets it in flat white, and in a
   direction where colour is rare the headline is not where to spend it.
   The italic that once marked "AI Agent" is gone too: one size, one weight,
   one style across all three lines, as the poster sets it. */

.hero-sub{
  font-size:clamp(15px, 1.25vw, 18px); line-height:1.65;
  color:var(--muted); max-width:58ch; margin-bottom:34px; font-weight:400;
}

.event-line{
  font-family:var(--mono); font-size:13px; letter-spacing:.01em;
  color:var(--faint); margin-top:0; padding-top:0; margin-bottom:38px;
}
.event-line strong{color:var(--text); font-weight:600;}
.event-line .sep{color:var(--line-hover); margin:0 10px;}

/* Countdown: numerals carry it, so the boxes go. */
.hero .countdown{margin:0 0 38px; padding:0; border:none;}
.hero .countdown-units{gap:40px;}
.hero .cd-unit{border:none; background:none; padding:0; min-width:0;}
.hero .cd-num{font-size:clamp(28px, 3.2vw, 44px); color:var(--text); line-height:1;}
.hero .cd-cap{color:var(--faint); margin-top:8px;}
.hero .countdown-label{color:var(--faint); margin-bottom:18px;}

/* The email capture, now the hero's single call to action. */
.hero .capture{padding:0; gap:0;}
.hero .capture-form{max-width:460px; flex:1 1 300px;}

@media (max-width:700px){
  /* A 2x2 countdown rather than four columns squeezed onto a phone. */
  .hero .countdown-units{display:grid; grid-template-columns:1fr 1fr; gap:22px 28px;}
  .hero .capture-form{max-width:none; flex:1 1 100%;}
  h1{max-width:none;}
  .hero-sub{margin-bottom:28px;}
  .event-line{margin-bottom:28px;}
}

/* ============================================================
   FOCUS — cobalt, site-wide
   ============================================================
   The old stylesheet gave focus rings to form fields only, so keyboard
   users lost the cursor the moment they tabbed onto a link or a button.
   :focus-visible rather than :focus, so a mouse click does not draw one.
   ============================================================ */
a:focus-visible, button:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--blue); outline-offset:3px; border-radius:2px;
}
::selection{background:var(--navy); color:var(--text);}

/* ============================================================
   HERO — rebuilt for Direction B
   ============================================================
   Order is headline -> subtext -> date/venue -> countdown -> email. Five
   elements where there were seven; the two path buttons moved to the
   Attend cards, which already describe exactly those two paths.

   Sized with clamp() rather than a desktop value plus overrides, so the
   headline is designed at every width instead of shrunk at three of them.
   ============================================================ */
h1{
  /* Back to 88px. The cap was lowered to 76 because "Tournament of Its
     Kind" measured 873px against a 760px column and spilled "Kind" onto a
     line of its own. The headline now breaks before "Its Kind" on purpose,
     so the longest line is "AI Agent Gaming" at 659px — well inside the
     column, and the size no longer has to compensate for a wrap. */
  font-size:clamp(40px, 7.2vw, 88px);
  line-height:.96;
  /* Tight, GitHub-style. Measured on github.com: 64px set at -2.24px,
     which is -0.035em. */
  letter-spacing:-0.035em;
  font-weight:600;
  max-width:16ch;
  margin-bottom:26px;
}
/* No colour on the headline. The poster sets it in flat white, and in a
   direction where colour is rare the headline is not where to spend it.
   The italic that once marked "AI Agent" is gone too: one size, one weight,
   one style across all three lines, as the poster sets it. */

.hero-sub{
  font-size:clamp(15px, 1.25vw, 18px); line-height:1.65;
  color:var(--muted); max-width:58ch; margin-bottom:34px; font-weight:400;
}

.event-line{
  font-family:var(--mono); font-size:13px; letter-spacing:.01em;
  color:var(--faint); margin-top:0; padding-top:0; margin-bottom:38px;
}
.event-line strong{color:var(--text); font-weight:600;}
.event-line .sep{color:var(--line-hover); margin:0 10px;}

/* Countdown: numerals carry it, so the boxes go. */
.hero .countdown{margin:0 0 38px; padding:0; border:none;}
.hero .countdown-units{gap:40px;}
.hero .cd-unit{border:none; background:none; padding:0; min-width:0;}
.hero .cd-num{font-size:clamp(28px, 3.2vw, 44px); color:var(--text); line-height:1;}
.hero .cd-cap{color:var(--faint); margin-top:8px;}
.hero .countdown-label{color:var(--faint); margin-bottom:18px;}

/* The email capture, now the hero's single call to action. */
.hero .capture{padding:0; gap:0;}
.hero .capture-form{max-width:460px; flex:1 1 300px;}

@media (max-width:700px){
  /* A 2x2 countdown rather than four columns squeezed onto a phone. */
  .hero .countdown-units{display:grid; grid-template-columns:1fr 1fr; gap:22px 28px;}
  .hero .capture-form{max-width:none; flex:1 1 100%;}
  h1{max-width:none;}
  .hero-sub{margin-bottom:28px;}
  .event-line{margin-bottom:28px;}
}

/* ============================================================
   ARC MOTIF — the logo, enormous, behind the games
   ============================================================
   The memorable moment, kept restrained for Direction B: the logo's two
   arcs scale up far past the viewport and pass each other behind the
   section, red entering from the left and blue leaving to the right. At
   4-6% opacity it reads as structure rather than decoration — you notice
   it on the second visit, not the first.

   aria-hidden and pointer-events:none: it is atmosphere, and it must never
   intercept a click or be announced.
   ============================================================ */
.arc-stage{position:relative; overflow:hidden;}
.arc-stage > .wrap{position:relative; z-index:2;}
.arc-motif{
  position:absolute; pointer-events:none; z-index:1;
  /* Smaller and offset, not centred. At 1500px dead-centre it stopped being
     atmosphere and became a large dark disc sitting in the middle of the
     section — the thing the brief calls a wash. Pushed off the right edge so
     only one arc is ever fully in frame. */
  width:min(85vw, 820px); aspect-ratio:1; opacity:.05;
  right:-14%; top:50%; transform:translateY(-50%);
}
.arc-motif svg{width:100%; height:100%; display:block;}
@media (prefers-reduced-motion: no-preference){
  /* One very slow rotation. 180s is deliberately past the point of being
     watchable — it should never catch the eye mid-scroll. */
  .arc-motif{animation:arc-turn 180s linear infinite;}
  @keyframes arc-turn{
    from{transform:translate(-50%,-50%) rotate(0deg);}
    to{transform:translate(-50%,-50%) rotate(360deg);}
  }
}

/* ============================================================
   BRAND MARK
   ============================================================
   The tournament logo replaces the constellation glyph the original build
   used as a placeholder. Shipped as a file rather than inlined four times
   so a new logo is one file swap, not four edits.

   No border or box around it: the old .brand-mark drew a gold-bordered
   square, and a frame around a logo that already has its own silhouette
   reads as part of the logo.
   ============================================================ */
.brand-mark-img{
  width:34px; height:34px; margin-right:12px; flex-shrink:0; display:block;
}
.foot-brand .brand-mark-img{margin-right:12px;}
@media (max-width:480px){ .brand-mark-img{width:30px; height:30px;} }

/* ============================================================
   NAV BREAKPOINT
   ============================================================
   Raised from 860px. Moving the "UCLA Trustworthy AI Lab × SAIR" line into
   the brand block widened it by ~120px, which pushed the links onto a second
   row between roughly 870 and 1000px — the nav bar rendered 120px tall
   instead of 76px. The collapse has to happen before the wrap, not after.

   Raised again to 1100px. Seven links plus a wordmark that gained the word
   "Gaming" no longer fit a 1000px viewport with any room to breathe; the
   bar technically held, but the links ran shoulder to shoulder with both
   flanks. Collapsing a little earlier is better than a cramped row.
   ============================================================ */
@media (max-width:1100px){
  nav .wrap{flex-wrap:nowrap;}
  .nav-links{
    display:none; position:absolute; top:100%; left:0; right:0;
    flex-direction:column; background:var(--elev); border-bottom:1px solid var(--line);
    padding:4px 32px 16px; max-height:calc(100vh - 76px); overflow-y:auto;
  }
  .nav-links.open{display:flex;}
  .nav-links{gap:0;}
  .nav-links a{padding:13px 0; border-bottom:1px solid var(--line);}
  .nav-links .nav-cta{display:block; margin-top:14px; text-align:center; padding:12px; border-bottom:none;}
  .nav-toggle{display:flex;}
  .nav-right .btn{display:none;}
  .brand{margin-right:0;}
}

/* ============================================================
   SPONSORS PAGE — tighter vertical rhythm
   ============================================================
   The 100px section padding is right for the homepage, where sections are
   long and need separating. On the sponsors page it stacked: 100px off the
   bottom of the form's section plus 100px off the top of the next one put
   200px of nothing between the form and "Current Sponsors", and 142px
   between the intro and the form.

   Scoped to this page rather than changing `section` globally — the
   homepage's rhythm is doing its job and shortening it there would crowd
   sections that genuinely are long.
   ============================================================ */
.section-tight{padding:52px 0;}
.section-tight .form-card{margin-top:24px;}
.section-tight .section-head{margin-bottom:32px;}
@media (max-width:760px){
  .section-tight{padding:40px 0;}
}

/* ============================================================
   GLASS HEADLINE
   ============================================================
   The reference renders its wordmark as frosted glass: the gradient shows
   through the letterforms, brighter along the upper-left strokes and
   dissolving toward the lower right.

   HOW, AND WHAT IS NOT POSSIBLE. `backdrop-filter` cannot be clipped to
   glyphs — it applies to an element's whole box, and `background-clip:text`
   does not constrain it. So there is no true per-letter backdrop blur in
   CSS. What does work, and is what the reference looks like anyway, is a
   translucent white gradient clipped to the text: the field behind reads
   through the letters, and the gradient gives the fall-off across them.

   `-webkit-text-stroke` supplies the bright edge on the strokes — that
   crisp rim is most of what makes it read as glass rather than as faded
   text.

   CONTRAST. Translucent type is where this technique usually fails an
   audit. The headline is 88px, so WCAG treats it as large text and the bar
   is 3:1 rather than 4.5 — measured below against the brightest point of
   the animated field, not assumed.
   ============================================================ */
/* Italic across all three lines, as asked for. Scoped to .hero — set on a
   bare `h1` it also caught the sub-pages' .page-head headings, which are
   not the tournament headline and should stay upright. Set on the element
   rather than an inner <em>, so it does not also pick up the gold that
   `h1 em` carries; the headline stays flat white. */
.hero h1{font-style:italic;}

.hero h1{
  background:linear-gradient(142deg,
    rgba(255,255,255,.97) 0%,
    rgba(255,255,255,.88) 32%,
    rgba(255,255,255,.66) 62%,
    rgba(255,255,255,.52) 100%);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
  -webkit-text-stroke:.6px rgba(255,255,255,.30);
  /* Lifts the letterforms off the field so the translucent fill still has
     an edge where the background behind it is light. */
  filter:drop-shadow(0 2px 18px rgba(10,12,16,.45));
}

@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  /* Without background-clip the fill would be `transparent` and the
     headline would vanish. Solid white instead — no glass, but legible. */
  .hero h1{ color:var(--text); -webkit-text-stroke:0; filter:none; }
}


/* ============================================================
   SPLIT SECTION HEAD — title left, lede far right
   ============================================================
   The default .section-head stacks the h2 over its sub-head. On the team
   page that leaves "Our Team" — two short words — sitting alone above a
   long sentence, so the block opens with a mostly empty line.

   Two columns instead: the title takes the left, and the lede sits in its
   own column held to the right edge of the container, with open space
   between them. That gap is the effect: the eye reads the title, crosses a
   clear space, and lands on the lede as a separate note rather than a
   subtitle.

   THE 48% IS A LINE COUNT, not a proportion. The column is right-aligned,
   so its width is what sets the lede's left edge — widening it pushes the
   text left and pulls the wrap up. Measured on both ledes: under ~44% each
   runs to four lines or more, over ~56% the sponsors lede drops to two and
   the two pages stop matching. 48% puts both at three lines with room on
   either side, so an edit to the copy will not flip one of them.

   `align-items:start`, so the two line up along their TOPS. The h2 and the
   lede are set at very different sizes, and their first lines meet within
   about a pixel of each other because the h2's tight 1.08 line-height
   pulls its cap up by roughly as much as the lede's looser leading pushes
   its own down.

   Scoped with its own class, not applied to .section-head globally — every
   other section head on the site is stacked and should stay that way.
   ============================================================ */
.section-head-split{
  display:grid; grid-template-columns:1fr minmax(0, 48%); gap:24px 48px;
  align-items:start;
}
/* The 16px under the h2 exists to separate it from the sub-head BELOW it.
   Side by side there is nothing below, and the margin only adds height to
   the head. */
.section-head-split h2, .section-head-split h1{margin-bottom:0;}
/* .page-head caps its own heading at 760px and its paragraph at 580px.
   Inside the split those caps fight the grid: the column already sets the
   measure, and the heading's cap in particular would stop it filling the
   left column. Specific enough to beat `.page-head h1` / `.page-head p`. */
.page-head .section-head-split h1,
.page-head .section-head-split p{max-width:none;}
/* Released from the global 560px cap: the column is already narrower than
   that, so the cap does nothing but risk fighting the grid later. */
.section-head-split .section-sub{max-width:none;}
/* The lede reads as the same note in both places, so it is set the same
   whether it is a .section-sub (team) or .page-head's bare <p> (sponsors). */
.page-head .section-head-split p{font-size:17px; color:var(--paper-dim);}

@media (max-width:860px){
  /* Back to stacked. Below this the title and a 48% lede column are each
     down to ~380px, and the title has no width to spare beside it. */
  .section-head-split{grid-template-columns:1fr; gap:16px;}
}


/* ============================================================
   HERO ASIDE — the submission window
   ============================================================
   A second column in the hero, holding the dates entries are open. Three
   hairline rules bracket the label and the date: enough structure to read
   as a stamped record rather than a stray line of text, without adding
   another panel to a page whose one filled surface is the crimson button.

   `align-items:start` puts it alongside the HEADLINE. The text column runs
   on past it — event line, countdown, email capture — and centring the
   aside against all of that would leave it floating beside the form, next
   to nothing it relates to.

   The date is plain --text, not the headline's glass gradient: that
   treatment is the h1's alone, and a second element wearing it would read
   as a second headline.
   ============================================================ */
.hero-content{
  display:grid; grid-template-columns:minmax(0,1fr) auto; gap:56px;
  align-items:start;
}
.hero-aside{width:250px; text-align:right;}
.ha-rule{height:1px; background:var(--line-hover);}
.ha-label{
  display:block; padding:11px 0;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.17em;
  text-transform:uppercase; color:var(--faint);
}
.ha-date{
  padding:16px 0; font-size:44px; font-weight:600;
  letter-spacing:-0.035em; line-height:1; color:var(--text);
}
.ha-time{
  display:block; padding:0 0 12px; margin-top:-6px;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.12em;
  text-transform:uppercase; color:var(--muted);
}
.ha-link{
  display:block; padding:11px 0;
  font-family:var(--mono); font-size:10.5px; letter-spacing:.17em;
  text-transform:uppercase; color:var(--gold); text-decoration:none;
}
.ha-link:hover{color:var(--text);}
.ha-link:focus-visible{outline:2px solid var(--gold); outline-offset:2px;}

@media (max-width:900px){
  /* One column. The aside follows the text in source order, so it lands
     below the email capture — supplementary, which is what it is. Left-set
     to match everything above it; right-aligned against a full-width
     column it would look stranded. */
  .hero-content{grid-template-columns:1fr; gap:0;}
  .hero-aside{width:auto; max-width:250px; text-align:left; margin-top:40px;}
}


/* ============================================================
   EVENT LINE — the in-person tournament, given its weight back
   ============================================================
   This line is what the countdown counts down to, and it was set as the
   quietest thing in the hero: 13px mono in --faint, dimmer than the label
   above the clock. Both halves now carry real weight.

   The date sits in a filled crimson chip. Worth knowing what that costs:
   crimson was the page's single action colour, used only on the email
   button a few hundred pixels below, and a second solid crimson block in
   the same view means neither one is uniquely THE action. Chosen with
   that trade understood — the date is the loudest thing under the
   headline, which is the point.

   --on-red, not white by eye: white on #C73030 measures 4.4:1, under the
   4.5 AA needs at this size. The token is the pairing that passes.

   The venue leaves mono for 16px sans at full --text. Mono at 13px in
   --faint was setting the one fact a visitor must act on as fine print.
   ============================================================ */
.event-line{
  display:flex; align-items:center; flex-wrap:wrap; gap:12px 16px;
  font-family:var(--sans); letter-spacing:0;
  margin-top:0; padding-top:0; margin-bottom:38px;
}
/* font-family declared, not inherited: three earlier .event-line rules set
   the line in mono, so without this the chip would render as mono too. */
.ev-chip{
  font-family:var(--sans); font-size:15px; font-weight:700; letter-spacing:.02em;
  text-transform:uppercase; color:var(--on-red); background:var(--red);
  padding:9px 16px; border-radius:3px; white-space:nowrap;
}
.ev-venue{
  font-family:var(--sans); display:flex; align-items:center;
  font-size:16px; color:var(--text);
}
.ev-pin{width:15px; height:15px; flex-shrink:0; margin-right:9px; opacity:.7;}

@media (max-width:560px){
  /* Stacked: beside the chip the address wraps mid-street at this width,
     which reads worse than giving it its own line. */
  .event-line{display:block;}
  .ev-chip{display:inline-block; margin-bottom:12px;}
  .ev-venue{font-size:15px;}
}


/* ============================================================
   BANNER — a full-bleed bar under the nav
   ============================================================
   Used twice: the homepage's Discord invite (a link) and the sponsors
   page's next-tournament note (a statement). One construction for both,
   so a returning visitor reads the second as the same device rather than
   as a new kind of thing.

   Full-bleed and flush under the nav: on the homepage the hero's top
   padding moves onto .hero-content so the bar can meet the nav's bottom
   border with nothing between them. Inset as a card it would read as the
   first item of page content rather than as a standing notice.

   NOT in Discord blurple. The page spends colour on exactly one thing —
   the crimson email button, and now the crimson date chip — and a third
   brand colour in the first screen would flatten what is left of that
   hierarchy. The Discord mark carries the recognition on its own.

   The homepage bar sits above .hero-glow in z-order, but the glow shows
   through the translucent fill, so it picks up whatever the field is
   doing behind it rather than sitting on a flat grey stripe.
   ============================================================ */
.hero{padding-top:0;}
.hero-content{padding-top:64px;}

.banner{
  position:relative; z-index:2; display:block;
  background:rgba(255,255,255,.05);
  border-bottom:1px solid var(--line);
  transition:background-color var(--dur) var(--ease);
}
/* Hover belongs to the linked one only — a statement that lights up under
   the cursor promises a click it cannot honour. */
a.banner-link:hover{background:rgba(255,255,255,.085);}
.banner-inner{
  display:flex; align-items:center; justify-content:center; gap:11px;
  padding-top:13px; padding-bottom:13px;
  font-size:14px; color:var(--text);
}
.hb-mark{width:19px; height:19px; flex-shrink:0; opacity:.9;}
.hb-text{font-weight:500;}
/* Nudges right on hover, the same tell the member links use. */
.hb-arrow{width:15px; height:15px; flex-shrink:0; opacity:.65;
  transition:transform var(--dur) var(--ease), opacity var(--dur) var(--ease);}
.banner-link:hover .hb-arrow{transform:translateX(3px); opacity:1;}

@media (max-width:640px){
  /* The arrow is decorative once the text wraps — it stops reading as
     "this continues somewhere" and just costs a line. */
  .banner-inner{font-size:13px; gap:9px; text-align:center;}
  .hb-arrow{display:none;}
  .hero-content{padding-top:48px;}
}

/* ============================================================
   NAV — the Join button
   ============================================================
   The one thing in the bar that stays visible on mobile. Every other
   .nav-right button is hidden below 1100px and moves into the menu, but
   Join is the reason most visitors arrive, so it sits beside the toggle
   rather than one tap deeper. Compact there so the wordmark still fits. */
@media (max-width:1100px){
  .nav-right .nav-join{display:inline-flex; padding:8px 16px;}
}
/* In the menu too, but only once the bar has no room for it: at phone
   width the wordmark, Join and the toggle together need ~400px, and the
   bar has ~335. Below 560px Join moves into the menu under Watch Demos. */
@media (min-width:561px){
  .nav-links .nav-cta-join{display:none !important;}
}
@media (max-width:560px){
  .nav-right .nav-join{display:none;}
  .nav-links .nav-cta-join{margin-top:10px;}
}


/* ============================================================
   JOIN PAGE
   ============================================================
   Instructions, so it is set for READING, not scanning: one column at a
   ~68-character measure, a numbered rail down the left, and very few boxes.
   An earlier version ran the full 1116px with six kinds of bordered panel
   (kit, table, two path cards, callout, FAQ) and a 10.5px mono chip; with
   that many frames nothing read as primary and the page looked jumbled.
   Boxes are kept for things the reader compares or copies: the two
   hosting options and the code. Everything else is type and space. */
.join-col{max-width:840px;}

/* ---- head ------------------------------------------------------------ */
.join-head{padding:88px 0 0;}
.join-head h1{font-size:56px; line-height:1.02; margin-bottom:20px;}
.join-lede{font-size:19px; color:var(--muted); max-width:600px; line-height:1.55;}

.join-dates{
  list-style:none; margin-top:44px;
  display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); max-width:520px;
  border-top:1px solid var(--line); border-bottom:1px solid var(--line);
}
.join-dates li{padding:20px 20px 20px 0; display:flex; flex-direction:column; gap:4px;}
.join-dates li + li{padding-left:20px; border-left:1px solid var(--line);}
.join-date{font-size:22px; font-weight:600; letter-spacing:-0.01em; color:var(--text);}
.join-date-label{font-size:14px; color:var(--muted);}

/* ---- steps: a rail of numbers, joined by a hairline ------------------ */
.join-main{padding:72px 0 24px;}
.join-step{
  position:relative;
  display:grid; grid-template-columns:44px minmax(0,1fr); column-gap:32px;
  padding-bottom:72px;
}
/* The line runs from under one number to just above the next, so the
   numbers read as stops on one path rather than as separate badges. */
.join-step:not(:last-child)::before{
  content:''; position:absolute; left:21.5px; top:56px; bottom:12px;
  width:1px; background:var(--line-hover);
}
.join-num{
  width:44px; height:44px; border:1px solid var(--line-hover); border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family:var(--mono); font-size:15px; font-weight:600; color:var(--text);
  background:var(--canvas);
}
.join-num-day svg{width:18px; height:18px;}

/* h2 cap height lines up with the middle of the 44px number. */
.join-body h2{font-size:30px; line-height:1.2; margin:5px 0 26px;}
.join-body h3, .join-practice h3{
  font-family:var(--serif); font-size:19px; font-weight:600; color:var(--text);
  display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-bottom:18px;
}

/* ---- running text ---------------------------------------------------- */
.join-list{padding-left:24px; font-size:16px; line-height:1.7; color:var(--muted);}
.join-list > li{padding-left:6px;}
.join-list > li + li{margin-top:16px;}
.join-list > li::marker{font-family:var(--mono); font-size:13px; color:var(--faint);}
.join-bullets{list-style:disc;}
.join-body strong, .join-practice strong{color:var(--text); font-weight:600;}
.join-note{margin-top:22px; font-size:15px; color:var(--faint);}
.join-link{color:var(--blue-pale); text-decoration:underline; text-decoration-color:rgba(169,192,255,.4); text-underline-offset:3px;
  transition:color var(--dur-fast) var(--ease), text-decoration-color var(--dur-fast) var(--ease);}
.join-link:hover{color:var(--text); text-decoration-color:currentColor;}
.join-link:focus-visible{outline:2px solid var(--blue); outline-offset:2px; border-radius:2px;}

.join-body code, .join-practice code{
  font-family:var(--mono); font-size:.86em; color:var(--text);
  background:rgba(255,255,255,.06); border-radius:3px; padding:2px 6px;
}
.join-code{
  margin:14px 0 16px; padding:16px 20px; overflow-x:auto;
  background:var(--elev); border:1px solid var(--line); border-radius:6px;
  font-family:var(--mono); font-size:13.5px; line-height:1.75; color:var(--text);
}
.join-code code{background:none; padding:0; font-size:inherit; border-radius:0;}
/* A flag inside a sentence: never split after its dashes. */
.join-flag{white-space:nowrap;}

.join-soon{
  font-size:12.5px; font-weight:500; line-height:1.4;
  color:var(--muted); border:1px solid var(--line-hover); border-radius:100px; padding:2px 10px; white-space:nowrap;
}

/* Sign-up, not yet live: the button it will be, greyed, beside a note at
   body size rather than a tiny chip. */
.join-pending{display:flex; align-items:center; flex-wrap:wrap; gap:12px 18px; margin:14px 0 4px; font-size:15px; color:var(--faint);}
.join-btn-disabled{opacity:.45; cursor:not-allowed;}

/* Starter kit: one row, the repo name and a link. */
.join-kit{
  display:flex; align-items:center; flex-wrap:wrap; gap:12px 16px;
  margin:14px 0 4px; padding:14px 16px 14px 20px;
  background:var(--elev); border:1px solid var(--line); border-radius:6px;
}
.join-kit svg{width:20px; height:20px; color:var(--text); flex-shrink:0;}
.join-kit-name{flex:1 1 220px; min-width:0; font-family:var(--mono); font-size:13.5px; color:var(--text); overflow-wrap:anywhere;}

/* ---- Step 2: the two options ---------------------------------------- */
.join-options{display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:20px;}
.join-option{padding:28px; background:var(--surface); border:1px solid var(--line); border-radius:8px;}
.join-option-head{display:flex; align-items:center; flex-wrap:wrap; gap:10px; margin-bottom:10px;}
.join-option-head h3{margin:0; font-size:21px;}
.join-option-sum{font-size:15.5px; line-height:1.6; color:var(--muted); margin-bottom:22px;}
.join-facts{border-top:1px solid var(--line);}
.join-facts > div{padding:14px 0; border-bottom:1px solid var(--line);}
.join-facts > div:last-child{border-bottom:none; padding-bottom:0;}
.join-facts dt{font-size:13.5px; color:var(--faint); margin-bottom:2px;}
.join-facts dd{font-size:15px; line-height:1.55; color:var(--text);}

/* ---- Steps 3 and day: the two paths, one after the other ------------- */
.join-sub + .join-sub{margin-top:44px; padding-top:40px; border-top:1px solid var(--line);}

.join-callout{
  margin-top:28px; padding:20px 24px;
  border-left:2px solid var(--blue); background:rgba(77,125,255,.07); border-radius:0 6px 6px 0;
  font-size:15px; line-height:1.65; color:var(--muted);
}
.join-callout p + p{margin-top:10px;}
/* One link that must not be missed (the agent setup guide): bold, on a
   red-tinted marker, so it reads as the key takeaway of the callout. */
.join-highlight{
  color:var(--text); font-weight:600; text-decoration:underline; text-underline-offset:3px;
  background:rgba(199,48,48,.28); padding:2px 6px; border-radius:4px;
  box-decoration-break:clone; -webkit-box-decoration-break:clone;
}
.join-highlight:hover{background:rgba(199,48,48,.42);}

/* ---- practice: claiming on the AltruAgent platform ------------------
   Not a tournament step, so it sits OFF the numbered rail, after it, set
   apart by a rule and a step down in heading size. Its text aligns with
   the page edge rather than the rail's text column for the same reason. */
.join-practice{padding:24px 0 64px;}
.join-practice .join-col{border-top:1px solid var(--line); padding-top:64px;}
.join-practice h2{font-size:30px; line-height:1.2; margin-bottom:16px;}
.join-practice-lede{font-size:16px; line-height:1.7; color:var(--muted); max-width:720px;}
.join-practice .join-callout{margin-top:24px;}
.join-practice-part{margin-top:44px;}

/* Troubleshooting: what you see on the left, what to do on the right. */
.join-trouble{border-top:1px solid var(--line);}
.join-trouble > div{
  display:grid; grid-template-columns:minmax(0,15em) minmax(0,1fr); gap:6px 32px;
  padding:16px 0; border-bottom:1px solid var(--line);
}
.join-trouble dt{font-size:15.5px; font-weight:600; color:var(--text); line-height:1.6;}
.join-trouble dd{font-size:15.5px; color:var(--muted); line-height:1.65;}

/* ---- end ------------------------------------------------------------- */
.join-end{padding:8px 0 104px;}
.join-faq{
  display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:24px 40px;
  padding:36px 40px; background:var(--surface); border:1px solid var(--line); border-radius:8px;
}
.join-faq > div{flex:1 1 320px;}
.join-faq h2{font-size:28px; margin-bottom:8px;}
.join-faq p{font-size:16px; line-height:1.6; color:var(--muted);}

/* ---- narrower -------------------------------------------------------- */
@media (max-width:760px){
  .join-head{padding-top:56px;}
  .join-head h1{font-size:40px;}
  .join-lede{font-size:17px;}
  .join-options{grid-template-columns:minmax(0,1fr);}
  .join-main{padding-top:56px;}
}
@media (max-width:560px){
  .join-trouble > div{grid-template-columns:minmax(0,1fr);}
  .join-practice h2{font-size:25px;}
  .join-practice .join-col{padding-top:48px;}
  /* The rail costs 76px a phone cannot spare; the number sits above its
     heading instead and the line goes. */
  .join-step{grid-template-columns:minmax(0,1fr); row-gap:16px; padding-bottom:56px;}
  .join-step:not(:last-child)::before{display:none;}
  .join-num{width:36px; height:36px; font-size:13px;}
  .join-body h2{font-size:25px; margin:0 0 22px;}
  .join-list{font-size:15.5px; padding-left:22px;}
  .join-option{padding:22px;}
  .join-code{padding:14px 16px; font-size:12.5px;}
  .join-faq{padding:26px 22px;}
  .join-faq h2{font-size:24px;}
  .join-date{font-size:19px;}
}

/* ============================================================
   IN-PERSON NOTE — the first-100 invitation, under the event line
   ============================================================
   Sits directly beneath the date and venue because it qualifies them:
   who gets to be in that room. A red-tinted panel so it reads as a
   notice, but not a second solid crimson block — the chip above already
   spends that.
   ============================================================ */
.event-line{margin-bottom:22px;}
.inperson-note{
  display:inline-flex; align-items:center; gap:12px;
  margin:0 0 34px; padding:12px 18px;
  font-family:var(--sans); font-size:16px; line-height:1.4; color:var(--text);
  background:rgba(199,48,48,.12);
  border:1px solid rgba(199,48,48,.45); border-left:3px solid var(--red);
  border-radius:3px;
}
.inperson-note strong{color:var(--red-text); font-weight:700;}
@media (max-width:560px){ .inperson-note{font-size:15px; padding:11px 14px;} }

/* A short status note under a game card's description. */
.game-note{font-size:.875rem;font-style:italic;opacity:.75;margin-top:.25rem;}
