﻿/* ============================================================================
   Golf Day HQ - the public site.

   Owns its own look. Loads NO Velzon, no DevExtreme, no jQuery: those exist for
   an admin workstation and this is a page a golfer opens on their phone. The old
   golf-public.css re-themed Velzon inside a scope; this replaces it rather than
   arguing with it.

   THE IDEA: a starter's tee sheet. Every club has one pinned by the first tee -
   a ruled chronological list, dates in a firm left-hand column, one line per
   game. It is the subject's own artifact rather than the card grid every events
   site reaches for, it scans on a phone the way a grid does not, and it is
   already sorted by date, which is how people actually look for a golf day.

   The one thing a browser needs to know is whether they can still enter. That is
   what the right-hand column is for, and it is the only place any colour other
   than the brand green appears.

   Type is HK Grotesk, already in wwwroot/fonts. The brand token names Inter, but
   Inter was being pulled from Google on every public page - which sends every
   anonymous visitor's IP to a third party the privacy notice does not mention.
   A local face removes that, and removes a render-blocking request.
   ============================================================================ */

@font-face {
    font-family: "HK Grotesk";
    src: url("../fonts/hkgrotesk-regular.woff2") format("woff2"), url("../fonts/hkgrotesk-regular.woff") format("woff");
    font-weight: 400;
    font-display: swap;
}

@font-face {
    font-family: "HK Grotesk";
    src: url("../fonts/hkgrotesk-medium.woff2") format("woff2"), url("../fonts/hkgrotesk-medium.woff") format("woff");
    font-weight: 500;
    font-display: swap;
}

@font-face {
    font-family: "HK Grotesk";
    src: url("../fonts/hkgrotesk-semibold.woff2") format("woff2"), url("../fonts/hkgrotesk-semibold.woff") format("woff");
    font-weight: 600;
    font-display: swap;
}

@font-face {
    font-family: "HK Grotesk";
    src: url("../fonts/hkgrotesk-bold.woff2") format("woff2"), url("../fonts/hkgrotesk-bold.woff") format("woff");
    font-weight: 700;
    font-display: swap;
}

:root {
    /* Brand, from the design tokens. Not negotiable. */
    --green: #21c17b;
    --green-deep: #30a672;
    /* The surface for WHITE text. The brand green measures 2.3:1 under white, so anything that
       carries white ink sits on this instead: 6.1:1. Decided by Johan 2026-08-29 (Q4): darker
       green under white, not dark ink on bright green. */
    --green-ink-bg: #15704b;
    --green-ink-bg-hover: #125c3e;

    /* A third green, for TEXT on white only. The brand pair are fill colours: the
       lighter measures about 2.4:1 as text and the deeper about 3.1:1, both under
       the 4.5:1 floor. This one clears it while staying recognisably the brand. */
    --green-text: #15704b;

    /* A deep green-black rather than #000. Pure black on a long ruled list is
       harsh, and this keeps the page in the brand's own family. */
    --ink: #12211a;
    --ink-soft: #55605a;

    /* Neutral and very light, matching the hi-fi app screens: white cards on a
       barely-there grey, not a tinted or cream ground. */
    --paper: #f5f6f7;
    --card: #ffffff;
    --rule: #e2e2e2;
    --rule-strong: #c9cccd;

    /* The only non-brand colour on the page, and it appears in exactly one
       place: a day whose entries are about to close. Spending the boldness once. */
    --signal: #a4161a;

    --shadow: 0 1px 5px #00000017;
    --radius: 10px;
    --measure: 68rem;
}

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

/* Full-height column so the footer sits at the bottom of the VIEWPORT on a short
   page - a sign-in card or an empty directory - instead of floating mid-screen with
   dead space under it. main takes the slack; on a long page it simply grows and
   the footer follows the content as normal. min-height, not height, so nothing
   is ever clipped. */
body.golf-site {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--paper);
    color: var(--ink);
    font-family: "HK Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: 1rem;
    line-height: 1.55;
    font-weight: 400;
}

/* Scoped to prose, NOT every anchor.

   A blanket `.golf-site a` rule is two classes of specificity and beats any
   component that sets its own colour with one - which silently repainted the
   wordmark, the buttons, the tabs and the filter toggle green-on-light, all of
   them below the contrast floor. It looked fine in a screenshot; only measuring
   found it. Components own their colour; this covers links inside body copy. */
.golf-site p a,
.golf-site li a:not(.row-link),
.golf-site .prose a { color: var(--green-text); }

.golf-site p a:hover,
.golf-site li a:not(.row-link):hover,
.golf-site .prose a:hover { color: var(--ink); }

/* Dropping Velzon means dropping Bootstrap's utilities with it. This one is not
   optional: without it the search field's label renders as visible text. Anything
   else that quietly relied on a Bootstrap class will surface the same way. */
.visually-hidden {
    position: absolute !important;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Keyboard focus must be obvious on every interactive thing, not just links. */
.golf-site :focus-visible {
    outline: 3px solid var(--green-deep);
    outline-offset: 2px;
    border-radius: 3px;
}

/* Two shapes of page, and the slack has to go to whichever element is the body's
   flex child. On a page that POSTs, @Html.Form() wraps main in a <form>, so the
   FORM is the flex item and main sits one level down; growing main there does
   nothing because its parent will not grow. On a page with no form, main IS the
   flex item. The first probe of this bug ran on a form-less page, passed, and
   hid the failure - so both shapes are named explicitly here. */
.golf-site > form,
.golf-site > main { flex: 1 0 auto; display: flex; flex-direction: column; }
.golf-site > form > main { flex: 1 0 auto; }
.site-foot { flex-shrink: 0; }

.wrap {
    width: 100%;
    max-width: var(--measure);
    margin-inline: auto;
    padding-inline: 1.25rem;
}

/* ---------------------------------------------------------------- masthead */

.masthead {
    background: var(--card);
    border-bottom: 1px solid var(--rule);
}

.masthead-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .75rem 1rem;
    min-height: 68px;
    padding-block: .5rem;
}

/* The search sits between the wordmark and the nav on wide screens and drops to its own full
   row on narrow ones (order puts it last in the wrap). */
.masthead .search { flex: 1 1 14rem; max-width: 24rem; }
@media (max-width: 720px) {
    .masthead .search { order: 3; flex-basis: 100%; max-width: none; }
}

.wordmark {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    color: var(--ink);
    text-decoration: none;
    font-weight: 700;
    font-size: 1.125rem;
    letter-spacing: -.02em;
}

.wordmark svg { color: var(--green); flex: none; }
.wordmark:hover { color: var(--ink); }

.masthead-nav {
    display: flex;
    align-items: center;
    gap: 1.25rem;
}

/* :not(.btn) matters. A bare `.masthead-nav a` is two classes and beats `.btn-go`,
   which is one - so the button rendered soft grey on green at 2.8:1 while looking
   perfectly fine in a screenshot. Third time this pattern bit in this file; see
   the note at the top of the button section. */
.masthead-nav a:not(.btn) {
    color: var(--ink-soft);
    text-decoration: none;
    font-weight: 500;
    font-size: .95rem;
}

.masthead-nav a:not(.btn):hover { color: var(--ink); }

/* ------------------------------------------------------------------ button

   A STRUCTURAL RULE FOR THIS FILE, learned the hard way three times: a container
   rule written as `.container a` is two classes and silently out-specifies any
   single-class component inside it. Every time that happened here it repainted a
   button or a link to a colour nobody chose, and every time it looked correct in
   a screenshot and only showed up under getComputedStyle.

   So: containers style their own children with :not(.btn), and components own
   their colour. If you add a container that colours anchors, exclude components
   from it rather than raising specificity somewhere else to win the fight. */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    padding: .6rem 1.1rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    font: inherit;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
    /* Batch Q3 item 2, 2026-09-22: this was 40px tall (1.2 line-height + .6rem top/bottom padding
       + the 1px border this box-sizing:border-box includes), measured with getBoundingClientRect
       on the answer, team-code, takedown and opt-out pages - the ones on this layout without the
       roster's own generous sizing. 44px is the figure the rest of the product already meets. A
       size change only: padding, text and colour are untouched, so a flex-centred label just gets
       more room around it rather than a redrawn button. */
    min-height: 44px;
}

/* Green fill is the app's primary action - taken from the hi-fi mobile screens,
   not invented here.

   The GREEN on it is the one deviation. Those screens fill with the brand
   #21c17b and set white on it, which measures about 2.4:1 and fails AA. This
   fills with --green-ink-bg #15704b instead, which carries white at about 6:1.
   The brand green is not abandoned - it still paints the badges, the hero art
   and the wordmark, where nothing white sits on it. */
.btn-go {
    background: var(--green-ink-bg);
    color: #fff;
}

.btn-go:hover {
    background: var(--green-ink-bg-hover);
    color: #fff;
}

/* The dark pill, used for the primary action on the app's auth screens. Kept for
   moments where green would compete with a green-heavy page. */
.btn-ink {
    background: var(--ink);
    color: var(--card);
    border-radius: 999px;
    padding-inline: 1.4rem;
}

.btn-ink:hover { background: #000; color: var(--card); }

.btn-quiet {
    background: var(--card);
    border-color: var(--rule-strong);
    color: var(--ink);
}

.btn-quiet:hover { border-color: var(--ink); color: var(--ink); }

/* -------------------------------------------------------------------- hero */

.hero {
    position: relative;
    overflow: hidden;
    padding: 3.5rem 0 7.5rem;
    border-bottom: 1px solid var(--rule);
    background: var(--card);
}

.hero .wrap { position: relative; }

.hero-art {
    position: absolute;
    inset: auto 0 0 0;
    width: 100%;
    height: 9rem;
    display: block;
    pointer-events: none;
}

.hero h1 {
    margin: 0 0 .5rem;
    font-size: clamp(2rem, 5vw, 3.1rem);
    font-weight: 700;
    letter-spacing: -.035em;
    line-height: 1.05;
    max-width: 20ch;
}

.hero p {
    margin: 0;
    max-width: 52ch;
    color: var(--ink-soft);
    font-size: 1.0625rem;
}

/* ------------------------------------------------------------ filter strip */

.filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    padding: 1.25rem 0;
}

.tabs { display: flex; gap: .25rem; }

.tab {
    padding: .45rem .9rem;
    border-radius: 999px;
    text-decoration: none;
    color: var(--ink-soft);
    font-weight: 500;
    font-size: .95rem;
}

.tab:hover { background: #eceded; color: var(--ink); }

.tab[aria-current="page"] {
    background: var(--ink);
    color: var(--card);
}

.search {
    display: flex;
    min-width: 12rem;
    background: var(--card);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    overflow: hidden;
}

.search input {
    flex: 1;
    padding: .55rem .8rem;
    border: 0;
    background: transparent;
    font: inherit;
    color: var(--ink);
    min-width: 0;
}

.search input:focus { outline: none; }
.search:focus-within { border-color: var(--green-deep); box-shadow: 0 0 0 3px #21c17b33; }

.search button {
    padding: .55rem .9rem;
    border: 0;
    background: transparent;
    color: var(--ink-soft);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.search button:hover { color: var(--ink); }

.toggle {
    padding: .5rem .9rem;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--card);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: .9rem;
    font-weight: 500;
}

/* As a button (the division choice on "My team"), it keeps the page's type and reads as pressable. */
button.toggle { font-family: inherit; cursor: pointer; min-height: 44px; }

.toggle[aria-pressed="true"] {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--card);
}

/* ---------------------------------------------------------------- places
   "Golf days in your area". A row of province pills with counts. */
.places {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    margin: 0 0 1rem;
}

/* Towns sit UNDER the province they belong to and read as the smaller choice, because that is
   what they are - a second, optional narrowing rather than a rival to the strip above. Pulled up
   tight against the provinces so the two read as one control, not two. */
.places-towns {
    margin-top: -.6rem;
    padding-left: .2rem;
    font-size: .95em;
}

.places-towns .place {
    padding: .25rem .65rem;
    font-size: .82rem;
}

.place {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .8rem;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    background: var(--card);
    color: var(--ink-soft);
    text-decoration: none;
    font-size: .9rem;
    font-weight: 500;
}

.place:hover { color: var(--ink); border-color: var(--ink-soft); }

.place[aria-current="true"] {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--card);
}

.place .n {
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
    opacity: .8;
}

.flyer-place { color: var(--ink-soft); }

/* ---------------------------------------------------------------- reserve */
.reserve {
    margin-top: 1.5rem;
    padding: 1.25rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper);
}
.reserve h2 { margin: 0 0 .5rem; font-size: 1.05rem; }
.reserve p { margin: 0 0 .9rem; }
.form-check-row { display: flex; align-items: center; gap: .6rem; margin: .5rem 0 1rem; }
.form-check-row input { width: 1.1rem; height: 1.1rem; }
tr.spent td { color: var(--ink-soft); }

/* --------------------------------------------------------------- golf days */
.gd-cards { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); gap: 1rem; }
.gd-card { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--card); padding: 1rem 1.2rem; box-shadow: var(--shadow); }
.gd-card.spent { opacity: .6; }
.gd-head { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.gd-head h1, .gd-head h2 { margin: 0; flex: 1 1 100%; }
.gd-head .pill { border: 1px solid var(--rule-strong); border-radius: 999px; padding: .1rem .6rem; font-size: .8rem; color: var(--ink-soft); }
.gd-head .pill-status { background: var(--green-soft, #e3f6ec); color: var(--green-text); border-color: transparent; }
.gd-facts { color: var(--ink-soft); margin: .5rem 0 .75rem; }
.gd-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: .5rem 1rem; margin: 0 0 .75rem; }
.gd-stats dt { font-size: .75rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; }
.gd-stats dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.gd-actions { display: flex; flex-wrap: wrap; gap: .5rem; }

.count {
    margin: 0 0 .25rem;
    color: var(--ink-soft);
    font-size: .9rem;
}

/* ========================================================== THE FLYER WALL

   A marketplace: organisers advertise a golf day, players find one near them.
   That makes the FLYER the product. Someone designed that poster to sell their
   day - burying it under a text row would be like listing a house without the
   photograph.

   So the image leads and the words support it, and the crop is PORTRAIT. A golf
   day flyer is an A4 poster, not a hero banner: a 16:9 crop decapitates the
   title and loses the sponsor strip along the bottom, which is exactly the part
   the organiser cares about. 3:4 shows enough of a real flyer to read it.
   ========================================================================== */

.wall {
    list-style: none;
    margin: 0 0 3rem;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15.5rem, 1fr));
    gap: 1.5rem;
}

.flyer {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    overflow: hidden;
    color: var(--ink);
    text-decoration: none;
    transition: box-shadow .15s ease, transform .15s ease;
}

.flyer:hover {
    color: var(--ink);
    box-shadow: 0 6px 18px #0000001f;
    transform: translateY(-2px);
}

.flyer:hover .flyer-title { text-decoration: underline; text-underline-offset: 2px; }

.poster {
    position: relative;
    aspect-ratio: 3 / 4;
    background: #eceded;
    overflow: hidden;
}

.poster img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* No flyer uploaded. Rather than a broken-image grey box, show the date large -
   the one fact that always exists, set the way a scorecard box is. */
.poster-none {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    background: linear-gradient(160deg, #eef6f2, #e3efe9);
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.poster-none .m {
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.poster-none .d { font-size: 3.25rem; font-weight: 700; line-height: 1; letter-spacing: -.03em; }
.poster-none .y { font-size: .85rem; color: var(--ink-soft); }

/* The state sits ON the poster, so it is readable while scanning images rather
   than only after reading the words. */
.poster-state {
    position: absolute;
    left: .6rem;
    bottom: .6rem;
    right: .6rem;
}

.poster-state span {
    display: inline-block;
    padding: .3rem .65rem;
    border-radius: 999px;
    font-size: .8rem;
    font-weight: 600;
    backdrop-filter: blur(6px);
}

.poster-state .closing { background: #ffe4e4f0; color: var(--signal); }
.poster-state .today { background: var(--green); color: var(--ink); }
.poster-state .shut { background: #1c1c1ce6; color: #fff; }
.poster-state .open { background: #ffffffeb; color: var(--green-text); }

.flyer-body { padding: .9rem 1rem 1.1rem; display: flex; flex-direction: column; gap: .15rem; }

.flyer-date {
    font-size: .8rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--green-text);
    font-variant-numeric: tabular-nums;
}

.flyer-title {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -.015em;
    line-height: 1.3;
}

.flyer-where { margin: .1rem 0 0; color: var(--ink-soft); font-size: .9rem; }
.flyer-who { margin: 0; color: var(--ink-soft); font-size: .82rem; }

/* Closed and played recede without dropping under the contrast floor - opacity
   on the whole card would take the body text with it. */
.flyer.spent .poster img { filter: grayscale(.7); }
.flyer.spent .flyer-date { color: var(--ink-soft); }

/* ------------------------------------------------------------------- empty */

.empty {
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 3rem 1.5rem;
    text-align: center;
    margin-bottom: 3rem;
}

.empty h2 { margin: 0 0 .5rem; font-size: 1.3rem; font-weight: 600; }
.empty p { margin: 0 auto 1.25rem; max-width: 44ch; color: var(--ink-soft); }
.empty-actions { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; }

/* ------------------------------------------------------------------ footer */

.site-foot {
    border-top: 1px solid var(--rule);
    background: var(--card);
    padding: 1.5rem 0;
    color: var(--ink-soft);
    font-size: .875rem;
}

.site-foot-inner {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1.25rem;
    align-items: center;
    justify-content: space-between;
}

.site-foot a { color: var(--ink-soft); text-decoration: none; }
.site-foot a:hover { color: var(--ink); text-decoration: underline; }
.site-foot nav { display: flex; gap: 1.25rem; }

/* ------------------------------------------------------------------ mobile */

@media (max-width: 34rem) {
    /* One flyer per row on a phone. Two columns would make each poster too small
       to read, which defeats the reason the image leads. */
    .wall { grid-template-columns: 1fr; gap: 1.25rem; }
    .hero { padding: 2.5rem 0 5.5rem; }
    .hero-art { height: 6rem; }
    .filters { gap: .6rem; }
}

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

/* ============================================================ LISTING PAGE

   The page a player lands on from the wall. The poster is the reason they
   clicked, so it gets real size - shown WHOLE rather than cropped, because this
   is where somebody actually reads the sponsors, the format and the entry fee
   the organiser printed on it.

   Two columns on desktop: poster left, facts right, so a portrait flyer and a
   short fact list sit side by side instead of the facts being pushed a screen
   down. One column on a phone, poster first.
   ========================================================================== */

.back {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    margin: 1.5rem 0 1rem;
    color: var(--ink-soft);
    text-decoration: none;
    font-size: .9rem;
    font-weight: 500;
}

.back:hover { color: var(--ink); }

.listing {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: 2rem;
    align-items: start;
    margin-bottom: 3rem;
}

.listing-poster {
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    overflow: hidden;
}

/* contain, not cover. On the wall a crop is fine because the card is a thumbnail;
   here the flyer IS the content and cutting the bottom off would lose the
   sponsors and the entry details printed there. */
.listing-poster img {
    display: block;
    width: 100%;
    height: auto;
    max-height: 46rem;
    object-fit: contain;
    background: #eceded;
}

.listing-facts { min-width: 0; }

.listing-facts h1 {
    margin: 0 0 .75rem;
    font-size: clamp(1.6rem, 3.5vw, 2.25rem);
    font-weight: 700;
    letter-spacing: -.03em;
    line-height: 1.12;
}

.listing-state { margin-bottom: 1.25rem; }

.listing-state span {
    display: inline-block;
    padding: .35rem .75rem;
    border-radius: 999px;
    font-size: .875rem;
    font-weight: 600;
}

.listing-state .closing { background: #ffe4e4; color: var(--signal); }
.listing-state .today { background: var(--green); color: var(--ink); }
.listing-state .shut { background: #1c1c1c; color: #fff; }
.listing-state .open { background: #e4f7ee; color: var(--green-text); }

/* The organiser's own view of a listing they own. Deliberately NOT .listing-state, which is the
   public detail page's entry-status row - that rule pills EVERY span inside it, so reusing the name
   would have turned the explanatory hints below into pills too. */
.own-listing {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .75rem;
    margin: 0 0 1.5rem;
    padding: .9rem 1rem;
    border: 1px solid var(--rule);
    border-radius: 12px;
}

.own-listing-pill {
    display: inline-block;
    padding: .35rem .75rem;
    border-radius: 999px;
    font-size: .875rem;
    font-weight: 600;
}

.own-listing-pill.live { background: #e4f7ee; color: var(--green-text); }
.own-listing-pill.down { background: #ffe4e4; color: var(--signal); }

/* Full width so it lands on its own line under the pill and the button, never squeezed between. */
.own-listing .field-validation-error { flex-basis: 100%; margin: 0; }

/* Its own line under the pill and the trigger, so the question is never squeezed beside them. */
.own-listing-confirm {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
    padding-top: .75rem;
    border-top: 1px solid var(--rule);
}

.own-listing-confirm p { flex-basis: 100%; margin: 0; font-weight: 600; }

/* Both of these are toggled with the hidden ATTRIBUTE, and a display rule beats the browser's
   default [hidden] { display: none }. Without this the confirmation panel and the error line sit
   open on the page from the moment it loads. */
.own-listing-confirm[hidden],
.own-listing .field-validation-error[hidden] { display: none; }

.notice {
    padding: .9rem 1rem;
    border: 1px solid var(--rule);
    border-left: 3px solid var(--rule-strong);
    border-radius: 8px;
    background: var(--card);
    margin-bottom: 1.5rem;
    font-size: .95rem;
}

.facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: 1.1rem 1.5rem;
    margin: 0 0 1.75rem;
    padding: 1.25rem 0;
    border-block: 1px solid var(--rule);
}

.facts div { min-width: 0; }
.facts dt { margin: 0 0 .15rem; font-size: .78rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft); }
.facts dd { margin: 0; font-weight: 600; }

.about h2 { margin: 0 0 .4rem; font-size: .78rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft); }

/* pre-wrap keeps the organiser's own line breaks. It is still HTML-encoded by
   Razor - this is anonymous user input and must never be Html.Raw. */
.about p { margin: 0; white-space: pre-wrap; }

/* .form-hint is written for a form: a NEGATIVE top margin that tucks it under the field above.
   On this page it sits under a price list, a description or the whole reserve card, and the
   negative margin pulled it up over the text it was meant to follow (measured 2026-09-21: the
   "Contact the organiser" line over the price rows, the takedown line over the last line of the
   description). Inside the listing facts it is ordinary text with space above it. */
.listing-facts .form-hint { margin: .75rem 0 0; }
.listing-facts .facts .form-hint { margin: 0; }
.listing-facts .reserve { margin-bottom: 1.75rem; }
.listing-facts .listing-takedown { margin-top: 1.75rem; }

@media (max-width: 52rem) {
    .listing { grid-template-columns: 1fr; gap: 1.5rem; }
}

/* ================================================================== FORMS

   Two kinds of class appear here and they are not interchangeable.

   FRAMEWORK-EMITTED, and therefore not negotiable: .form-group, .form-control
   and .col-form-label come out of @Html.Model().FormGroupFor(...). Checklist E.4
   requires that helper - hand-rolling inputs to suit a stylesheet would be the
   wrong way round - so the stylesheet adapts to the helper's markup instead.

   OURS: everything else on these pages is written in the classes below rather
   than reimplementing a slice of Bootstrap's grid to satisfy old markup.
   ========================================================================== */

.panel {
    background: var(--card);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 1.75rem;
    margin: 2rem 0 3rem;
}

.panel-narrow { max-width: 44rem; margin-inline: auto; }
.panel-wide { max-width: 56rem; margin-inline: auto; }

.panel h1 { margin: 0 0 .4rem; font-size: 1.6rem; font-weight: 700; letter-spacing: -.025em; }
.panel > p { margin: 0 0 1.5rem; color: var(--ink-soft); }

.panel-centre { text-align: center; }
.panel-centre h1 { font-size: 1.5rem; }
.panel-centre > p { margin-inline: auto; max-width: 46ch; }

/* Two fields side by side where they belong together (a date and its closing
   date), collapsing on a phone. */
.pair {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: 0 1.25rem;
}

.form-group { margin-bottom: 1.1rem; }

.col-form-label,
.form-group > label {
    display: block;
    margin-bottom: .3rem;
    font-size: .875rem;
    font-weight: 600;
    color: var(--ink);
}

.form-control {
    display: block;
    width: 100%;
    padding: .6rem .8rem;
    border: 1px solid var(--rule-strong);
    border-radius: 8px;
    background: var(--card);
    color: var(--ink);
    font: inherit;
    line-height: 1.4;
}

.form-control::placeholder { color: #9aa3a0; }

.form-control:focus {
    outline: none;
    border-color: var(--green-deep);
    box-shadow: 0 0 0 3px #21c17b33;
}

/* A grouped question (the division radios on the reservation, answer and join pages) is a
   fieldset. Without this it wore the browser's default frame and indent, and its choices
   stacked as bold block labels - nothing like the single fields above it. */
fieldset.form-group { border: 0; padding: 0; margin-inline: 0; min-width: 0; }

fieldset.form-group > label {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    min-height: 44px;
    margin: 0 1.25rem 0 0;
    font-size: .95rem;
    font-weight: 500;
    cursor: pointer;
}

/* FormGroupFor draws a bool as a .form-group.form-check: the box, its label, then the hidden "False"
   that makes a clear box post something. Laid out like .form-check-row, box and label on one line;
   without this, .form-group > label above put the label on the line under the box. */
.form-group.form-check { display: flex; align-items: center; gap: .6rem; margin: .5rem 0 1rem; }
.form-group.form-check > label { margin-bottom: 0; }
.form-group.form-check > .form-check-input { flex: none; width: 1.1rem; height: 1.1rem; }

/* A field with a button beside it: the handicap box and "Look it up" on the invitation answer and
   team-link pages. The box takes the room, and on a narrow phone the button wraps under it rather
   than squeezing it. The button carries the field's own bottom margin, so their bottoms line up. */
.field-with-action { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0 .75rem; }
.field-with-action > .form-group { flex: 1 1 10rem; min-width: 0; }
.field-with-action > .btn { margin-bottom: 1.1rem; }

/* Reserve.cshtml: one row per other player - a name box drawn like .form-control, then its two
   division choices. On a phone the name takes the whole line and the choices sit under it. */
.player-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .2rem 1.25rem;
    margin-bottom: .6rem;
}

.player-row input[type="text"] {
    flex: 1 1 14rem;
    min-width: 0;
    padding: .6rem .8rem;
    border: 1px solid var(--rule-strong);
    border-radius: 8px;
    background: var(--card);
    color: var(--ink);
    font: inherit;
    line-height: 1.4;
}

.player-row input[type="text"]:focus {
    outline: none;
    border-color: var(--green-deep);
    box-shadow: 0 0 0 3px #21c17b33;
}

.player-row label {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    min-height: 44px;
    font-size: .95rem;
    cursor: pointer;
}

@media (max-width: 480px) {
    .player-row input[type="text"] { flex-basis: 100%; }
}

textarea.form-control { min-height: 7rem; resize: vertical; }
input[type="file"].form-control { padding: .45rem .6rem; }

/* Validation. The framework writes these classes onto the input and the span, so
   the failure has to be legible without any extra markup in the view. */
.form-control.input-validation-error { border-color: var(--signal); }

/* The framework renders the validation span and Bootstrap's .invalid-feedback for
   EVERY field, valid or not, and relies on Bootstrap to hide them until something
   fails. Without Bootstrap they were all visible on a blank form - every field
   announcing it was required before anyone had typed. Hidden here, and revealed
   only when the input actually carries the error class. */
.field-validation-valid { display: none; }
.invalid-feedback { display: none; }
.form-control.input-validation-error ~ .invalid-feedback,
.form-control.input-validation-error + .field-validation-error { display: block; }

.field-validation-error,
.validation-summary-errors {
    display: block;
    margin-top: .3rem;
    color: var(--signal);
    font-size: .875rem;
    font-weight: 500;
}

.validation-summary-errors {
    margin: 0 0 1.25rem;
    padding: .9rem 1rem;
    border: 1px solid #f0c9c9;
    border-left: 3px solid var(--signal);
    border-radius: 8px;
    background: #fdf4f4;
}

.validation-summary-errors ul { margin: 0; padding-left: 1.1rem; }
.validation-summary-valid { display: none; }

.form-hint { margin: -.6rem 0 1.1rem; color: var(--ink-soft); font-size: .875rem; }

.form-actions { display: flex; gap: .6rem; flex-wrap: wrap; margin-top: 1.5rem; }

/* Consent block above the submit button. Body colour, not a muted grey: this is
   the sentence somebody is agreeing to, and the last version of it rendered at
   1.43:1 because it borrowed a component whose variant paints its own text. */
.consent {
    padding: 1rem;
    border: 1px solid var(--rule);
    border-radius: 8px;
    margin-top: 1.5rem;
    font-size: .9rem;
    color: var(--ink);
}

/* ------------------------------------------------------------------ tables */

.data-table { width: 100%; border-collapse: collapse; margin-bottom: 1.5rem; }
.data-table caption { text-align: left; }
.data-table th,
.data-table td { padding: .6rem .75rem; text-align: left; border-bottom: 1px solid var(--rule); vertical-align: top; }
.data-table thead th { background: #f7f8f8; font-size: .8rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); }

/* "My team" (/invite/teams). The captain's DIVISION column holds two 44px buttons per row, and on a
   390px phone the row ran past the card's right edge (QA J5 NOTE-5). The pair wraps onto two lines
   when it has to, and the cells give back the padding a phone cannot spare. */
.team-division { display: flex; flex-wrap: wrap; gap: .35rem; }
.team-table td:first-child { overflow-wrap: anywhere; }
@media (max-width: 480px) {
    .team-table th,
    .team-table td { padding: .5rem .3rem; }
    .team-table thead th { letter-spacing: 0; font-size: .7rem; }
    .team-division .toggle { padding: .4rem .6rem; }
}

/* The captain's check-in at the registration desk (/invite/check-in), used standing up on a phone:
   one name per line with its button on the same line, the button never squeezed below 44px, and a
   long name wrapping rather than pushing the button off the card. */
.checkin-ball { margin-bottom: 1.5rem; }
.checkin-ball h2 { font-size: 1.05rem; display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; }
.checkin-list { list-style: none; margin: 0; padding: 0; }
.checkin-list li { display: flex; align-items: center; justify-content: space-between; gap: .75rem; padding: .6rem 0; border-bottom: 1px solid var(--rule); }
.checkin-name { min-width: 0; overflow-wrap: anywhere; }
.checkin-name small { display: block; color: var(--ink-soft); }
.checkin-list .btn { flex: 0 0 auto; min-width: 6.5rem; }
.checkin-done { padding: .6rem .8rem; border-radius: 8px; background: #d9f5e7; color: var(--green-text); }

.pill { display: inline-block; padding: .15rem .55rem; border-radius: 999px; font-size: .8rem; font-weight: 600; }
.pill-yes { background: #ffe9c9; color: #6b4708; }
.pill-no { background: #d9f5e7; color: var(--green-text); }

/* ------------------------------------------------------------------- prose */

.prose h2 { margin: 1.75rem 0 .5rem; font-size: 1.05rem; font-weight: 600; }
.prose p { margin: 0 0 .9rem; }
.prose p:last-child { margin-bottom: 0; }

/* ------------------------------------------------------------------ prices */
.prices { border: 1px solid var(--rule); border-radius: var(--radius); padding: 1rem 1.25rem; margin: 1.5rem 0; background: var(--card); }
.prices h2 { font-size: 1.05rem; margin: 0 0 .5rem; }
.price-list { display: grid; grid-template-columns: auto auto; gap: .3rem 1.25rem; justify-content: start; margin: 0; }
.price-list dt { color: var(--ink-soft); }
.price-list dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }

/* The Advanced disclosure on the listing forms.

   A native <details>. Everything inside stays in the DOM when it is shut, so the form keeps
   posting the values it already has - see the note in Submit.cshtml. */
.advanced {
    border: 1px solid var(--rule);
    border-radius: 6px;
    background: var(--paper);
    margin: 1.5rem 0;
}

.advanced > summary {
    cursor: pointer;
    padding: .85rem 1rem;
    font-weight: 600;
    list-style: none;
    display: flex;
    align-items: baseline;
    gap: .6rem;
    flex-wrap: wrap;
    border-radius: 6px;
}

/* The default marker is a black triangle in Safari and a rotated one in Chrome. Replace it with
   one that follows the open state in both, and keep the row clickable across its whole width. */
.advanced > summary::-webkit-details-marker { display: none; }
.advanced > summary::marker { content: ""; }

.advanced > summary::before {
    content: "\203A";
    display: inline-block;
    font-size: 1.25em;
    line-height: 1;
    color: var(--green-text);
    transition: transform .15s ease;
}

.advanced[open] > summary::before { transform: rotate(90deg); }

@media (prefers-reduced-motion: reduce) {
    .advanced > summary::before { transition: none; }
}

.advanced > summary:hover { background: var(--card); }
.advanced > summary:focus-visible { outline: 3px solid var(--green-deep); outline-offset: 2px; }

.advanced-hint {
    font-weight: 400;
    font-size: .9rem;
    color: var(--ink-soft);
}

/* The body sits inside the same box, separated by a rule rather than a second card. */
.advanced[open] > summary { border-bottom: 1px solid var(--rule); border-radius: 6px 6px 0 0; }
.advanced > *:not(summary) { padding-left: 1rem; padding-right: 1rem; }
.advanced > *:nth-child(2) { padding-top: 1rem; }
.advanced > *:last-child { padding-bottom: 1rem; }


/* ---- the team roster: "which one is you?" -------------------------------------------------- */

/* Sized for the moment it is actually used: a phone held at arm's length on a first tee, in the
   sun, by somebody who may have left their reading glasses in the car. Every target clears 44px
   comfortably and the name carries the weight, because the name is the only thing being read. */
.roster {
    display: flex;
    flex-direction: column;
    gap: .5rem;
    margin-top: .75rem;
}

.roster-name {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    width: 100%;
    min-height: 56px;
    padding: .75rem 1rem;
    text-align: left;
    /* Batch Q3 item 3, 2026-09-22: a <button> does not inherit font-family from its ancestors in
       the browser's own UA stylesheet, and nothing here overrode that, so the roster rendered in
       the platform's default form-control face (bold, Arial-like) while the rest of the site is
       HK Grotesk. The other buttons on this stylesheet are spared this because they carry the
       `.btn` class, which sets `font: inherit` for exactly this reason (see the button section
       above) - this custom, non-.btn button did not. */
    font: inherit;
    font-size: 1.15rem;
    line-height: 1.3;
    background: var(--card);
    border: 1px solid var(--rule-strong);
    border-radius: 6px;
    cursor: pointer;
}

.roster-name:hover { border-color: var(--green-text); }
.roster-name:focus-visible { outline: 3px solid var(--green-deep); outline-offset: 2px; }

/* The seat this browser already holds. Marked rather than removed: somebody who tapped the wrong
   name has to be able to see that and pick again, which is impossible if their own row vanishes. */
.roster-name-me {
    border-color: var(--green-text);
    box-shadow: inset 3px 0 0 var(--green-text);
}

/* The captain's seat and a seat already taken (ruling R18): shown, so the list matches the start
   sheet, but not a button. Flat and without the pointer so nobody reads it as something to press. */
.roster-name-taken {
    cursor: default;
    background: transparent;
    border-style: dashed;
    color: var(--ink-soft);
}

/* The two division buttons on the "is this you?" step of a claim. Side by side, each a full target. */
.claim-divisions {
    display: flex;
    gap: .5rem;
    margin-top: .75rem;
}
.claim-divisions .roster-name { justify-content: center; }
.claim-question { font-size: 1.05rem; }
.claim-back { margin-top: 1.25rem; }

.roster-name-text { font-weight: 600; }

.roster-name-state {
    font-size: .85rem;
    color: var(--ink-soft);
    white-space: nowrap;
}

/* The public leaderboard (/board/{token}). Numbers line up in columns; the name takes the room. */
.board-table td:nth-child(1),
.board-table td:nth-child(3),
.board-table td:nth-child(4) { font-variant-numeric: tabular-nums; white-space: nowrap; }
.board-table td:nth-child(4) { font-weight: 700; }

/* ?tv=1, for a clubhouse screen read from across the room: no site chrome, large type. */
.board-tv .masthead,
.board-tv .site-foot { display: none; }
.board-tv .public-board { max-width: none; }
.board-tv .public-board h1 { font-size: 2.6rem; }
.board-tv .board-table { font-size: 1.6rem; }

/* Ruling R22: a cancelled golf day's listing stays up and says so, in red, on the card and the page. */
.listing-cancelled {
    margin: .5rem 0;
    padding: .55rem .8rem;
    border: 1px solid #f0c9c9;
    border-left: 4px solid #c0392b;
    border-radius: 6px;
    background: #fdf4f4;
    color: #a12a1f;
    font-weight: 700;
}

/* The programme of a golf day (players-know-the-programme): Views/Shared/_DayProgramme.cshtml, on the
   listing and the invitation answer page. A time column and what happens, one item to a row. */
.day-programme { margin-block: 1.25rem; }
.day-programme h2 { margin: 0 0 .2rem; font-size: .78rem; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft); }
.day-programme-date { margin: 0 0 .5rem; font-weight: 600; }
.day-programme-items { list-style: none; margin: 0; padding: 0; }
.day-programme-items li { display: grid; grid-template-columns: 4rem 1fr; gap: .75rem; padding-block: .45rem; border-top: 1px solid var(--rule); }
.day-programme-time { font-weight: 700; font-variant-numeric: tabular-nums; }
.day-programme-detail { display: block; color: var(--ink-soft); white-space: pre-wrap; }
