/* ==========================================================================
   Pine Royale :: homepage concept

   Cream-dominant, warm-char ink, Royale Red as the single accent.
   Bootstrap 5.3 supplies the grid and offcanvas. Nothing else.

   Structure, weights and the button system follow the Callahan and Pacesetter
   prototypes. The feeling does not: Callahan is a cream resort, this is a
   nine-hole course with a mini golf and a burger.

   NO GREEN FIELDS. The guide's darks are all olive, and an olive field beside
   Royale Red reads as Christmas. Green survives only inside Roy's own artwork.

   THE MARKUP RULE
   Headings, paragraphs and lists carry NOTHING — no classes, no data
   attributes. A class sits on the wrapper and every h2 / p / ul inside is
   reached by nesting. Component wrappers carry classes normally.

   Section padding lives on the band wrapper, never on the block.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root{
  /* Brand, from the November 2024 guide */
  --royale:     #ff4632;  /* PMS 3556 C. The accent, and the only loud thing. */
  --royale-ink: #c4301d;  /* derived: red that holds contrast on cream */
  /* Same value, different job. --royale stays the vivid brand red for every
     SMALL use — buttons, eyebrows, the checkerboard, Roy's flag — where it is
     a fill behind white text and reads as loud as ever. This deeper one is
     only for the big FIELDS, because cream on #ff4632 is 2.89:1 and no amount
     of wanting fixes that. On #c4301d cream is 4.72:1. */
  --royale-deep:#c4301d;
  --cream:      #f2edd7;  /* the guide's off-white. The dominant ground. */
  --shell:      #faf7e9;  /* the lightest paper */
  --sand:       #dcd0a8;  /* a tone under cream, for edges and the warm band */
  --clay:       #b8805d;  /* PMS 7514 C */
  --rust:       #7d472b;  /* PMS 7591 C */
  --pine:       #363718;  /* PMS 574 C — ink only, never a background */

  /* derived: a warm near-black. The guide has no neutral dark, only olives,
     and olive fields make the red festive. This is the fix. */
  --char:       #241d12;
  --char-soft:  #2f2718;

  /* surface tokens, swapped per band */
  --bg:      var(--cream);
  --fg:      var(--pine);
  --fg-soft: #6b6445;
  --hair:    rgba(54,55,24,.18);
  --accent:  var(--royale-ink);

  /* Anton carries the loud half: statements, headlines, the poster voice.
     Jost carries the quiet half: nav, labels, figures, buttons.
     Alfa Slab One is a heavy Clarendon — the American signage face, the one
     on ballparks, fairgrounds, diner menus and golf scorecards. Anton was
     here first and was wrong: it is a 2011 condensed grotesque with no
     period cues, and its vintage reading on Patty's comes entirely from the
     script, the checkerboard and the colour around it. A slab agrees with a
     monoline script; a modern grotesque argues with one. */
  --poster:  "Alfa Slab One","Clarendon","Rockwell",Georgia,serif;
  --display: "Jost","Futura","Avenir Next",system-ui,sans-serif;
  --body:    "source-sans-3","Source Sans 3",-apple-system,BlinkMacSystemFont,sans-serif;
  --script:  "beverly-drive-right","Grand Hotel",cursive;

  /* the checker cell lives here, not on .checker, because the band that the
     footer strip pushes up into has to reserve exactly two of them */
  --chk-cell: clamp(15px, 2.5vw, 36px);

  --pad-y:  clamp(4.5rem,8vw,8.5rem);
  --maxw:   1240px;   /* one source of truth: .wrap and --edge both read it */
  --gutter: clamp(1.25rem,4vw,4rem);
  --ease:   cubic-bezier(.22,.61,.36,1);
  --radius: 2px;
}

/* Five grounds. Cream carries the page, red appears once. */
.theme-cream{--bg:var(--cream);--fg:var(--pine);--fg-soft:#6b6445;--hair:rgba(54,55,24,.18);--accent:var(--royale-ink)}
.theme-shell{--bg:var(--shell);--fg:var(--pine);--fg-soft:#6b6445;--hair:rgba(54,55,24,.14);--accent:var(--royale-ink)}
/* Sand is the darkest light ground on the page, so ink that is comfortable on
   cream is merely *passing* here: --fg-soft measured 4.76:1 and the rust
   eyebrow 4.85:1 — over the 4.5 bar, but thin at 300 weight and visibly
   washed. The hairlines were the real failure at 1.48:1, effectively
   invisible. Ink darkened and rules strengthened; the ground is untouched
   because the warm band is the point. */
.theme-sand {--bg:var(--sand); --fg:#2d2714;--fg-soft:#4a4327;--hair:rgba(45,39,20,.4);--accent:#6b3a20}
/* a light body face on a mid-tone ground needs the extra stroke more than it
   needs the extra contrast */
.theme-sand .note p,.theme-sand .benefits li{font-weight:400}
.theme-char {--bg:var(--char); --fg:var(--cream);--fg-soft:#b0a98d;--hair:rgba(242,237,215,.18);--accent:var(--sand)}
/* Cream on Royale Red measures 2.89:1 — under the 3:1 large-text floor, and
   the 92%-alpha body copy was 2.64:1. Rather than darken the field (cream on
   #c4301d would be 4.72:1) the TYPE flips to ink: char on Royale Red is
   4.90:1, the red stays at full saturation, and dark-on-red is the more
   period-correct pairing anyway — it is what diner and drive-in signage does. */
.theme-red  {--bg:var(--royale-deep);--fg:var(--cream);--fg-soft:rgba(242,237,215,.92);--hair:rgba(242,237,215,.3);--accent:var(--cream)}

/* --------------------------------------------------------------------------
   2. BASE
   -------------------------------------------------------------------------- */
*{box-sizing:border-box}
html{overflow-x:clip;scroll-behavior:smooth}
body{
  margin:0;
  background:var(--cream);
  color:var(--pine);
  font-family:var(--body);
  font-size:1.0625rem;
  font-weight:400;
  line-height:1.72;
  overflow-x:clip;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
/* Headlines are POSTERS: heavy, condensed, uppercase, tight. The previous
   pass set them in Jost 400, which is elegant and completely quiet — the
   brand is neither. */
h1,h2{
  font-family:var(--poster);
  font-weight:400;
  text-transform:uppercase;
  letter-spacing:.005em;
  margin:0;
  text-wrap:balance;
  color:var(--fg);
}
h1,.headline{font-size:clamp(1.9rem,3.6vw,3.1rem);line-height:1.04}
h2{font-size:clamp(1.75rem,3.4vw,2.75rem);line-height:1.02}
h3,h4{
  font-family:var(--display);font-weight:500;
  letter-spacing:-.006em;margin:0;text-wrap:balance;color:var(--fg);
}
h3{font-size:clamp(1.25rem,1.8vw,1.55rem);line-height:1.2}
h4{font-size:1.05rem;line-height:1.3}
p{margin:0 0 1.15rem}
p:last-child{margin-bottom:0}
a{color:inherit;text-decoration:none}
img{max-width:100%;height:auto;display:block}
strong{font-weight:700}
.sprite{position:absolute;width:0;height:0;overflow:hidden}

:where(a,button,input):focus-visible{outline:2px solid var(--accent);outline-offset:4px;border-radius:var(--radius)}
.theme-char :where(a,button):focus-visible{outline-color:var(--cream)}
.theme-red :where(a,button):focus-visible{outline-color:var(--cream)}

/* --------------------------------------------------------------------------
   3. BANDS
   -------------------------------------------------------------------------- */
.band{background:var(--bg);color:var(--fg);padding-block:var(--pad-y);position:relative}
.band--tight{--pad-y:clamp(2.75rem,4.5vw,4.5rem)}
/* The footer checker rides two cells up into whatever band precedes it. On a
   wide screen that lands in empty margin; on a phone the centred CTA is nearly
   full-width and the strip clipped it, so the band buys the two cells back. */
main > .band:last-child{padding-bottom:calc(var(--pad-y) + var(--chk-cell) * 2)}
@media (max-width:720px){
  /* on a phone the CTA is nearly full-width, so it needs clear air under it,
     not just the two cells the strip occupies */
  main > .band:last-child{padding-bottom:calc(var(--pad-y) + var(--chk-cell) * 4)}
}
.band--roy{--pad-y:clamp(3.25rem,5.5vw,5.5rem)}
.wrap{max-width:var(--maxw);margin-inline:auto;padding-inline:var(--gutter)}
.wrap--narrow{max-width:1080px}

/* .intro — an eyebrow above a heading. Tried in the script once — it reads
   well in isolation, but an eyebrow runs six to eight times a page and the
   script stops being a signature when it is that frequent. Back to the caps. */
/* The eyebrow is styled off .intro, not off the tag inside it, because that tag
   changes: on a page whose real subject is the eyebrow ("Eat & Drink") rather
   than the display line ("Good golf deserves good grub"), the two swap semantic
   levels — h1 goes on the eyebrow and the display line becomes an h2. Nothing
   about how it LOOKS should move when that happens. */
.intro > :where(span,h1,h2,p){
  display:block;
  font-family:var(--display);font-size:.74rem;font-weight:500;
  letter-spacing:.24em;text-transform:uppercase;color:var(--accent);
  margin-bottom:1.3rem;line-height:1.6;
}

/* .note — the answering column. Lighter, softer, comfortable measure. */
.note{max-width:50ch}
.note p{font-size:clamp(1.02rem,1.25vw,1.18rem);font-weight:400;line-height:1.64;color:var(--fg-soft)}
.note p + p{margin-top:1.1rem}
.note strong{color:var(--fg);font-weight:700}

/* THE SCRIPT RULE
   Beverly Drive Right never sits inside a sentence set in another face — two
   faces in one headline read as a mistake, not a choice. It appears only as a
   standalone object: the wordmark, Roy's line, and Roy's name. Anything that
   is a sentence in a heading is set in the display face.
   (The live site sets the script on nearly every h1/h2/h3, which is exactly
   why that site has no hierarchy.) */

.rule{border:0;border-top:1px solid var(--hair);margin:0}

/* THE CHECKERBOARD — a border, used selectively.
   Full-width symmetrical strips read as a rule and balance nothing. Used as a
   border it should be a PATCH: partial width, offset, and alternating side
   down the page so the two instances counterweight each other rather than
   mirror. It straddles the join it sits on — half above, half below.

   --chk-cell sets the square; two rows is the guide's proportion
   (_checkers-green-2row on production, which also ships a three-row red). */
.checker{
  /* The square SCALES with the viewport rather than stepping at breakpoints —
     2.5vw, which lands on exactly 36px at 1440 and holds that as the ceiling,
     with a 15px floor so it never turns to noise on a phone. round() below
     still snaps the strip to whole squares at every size in between. */
  --chk-rows:3;
  position:absolute;z-index:4;
  height:calc(var(--chk-cell) * var(--chk-rows));
  /* The light square is a HOLE, not a cream tile — so the strip dissolves into
     whatever it crosses. Over the hero photograph the top row shows grass and
     treeline through it and only the bottom row lands on cream, which is what
     makes it read as applied to the picture rather than laid over it.
     --chk-light exists because that only works on a light ground: see the
     footer override below. */
  background-color:var(--chk-light,transparent);
  /* conic-gradient, not two offset 45deg linear-gradients. The linear version
     is the common recipe and it is subtly wrong: its colour stops sit on a
     DIAGONAL axis, so the renderer antialiases along that diagonal and draws a
     faint 45-degree seam through every square. A conic gradient's quadrant
     boundaries are horizontal and vertical, so the edges land on the pixel
     grid and the squares come out clean. One layer instead of two, as well. */
  background-image:conic-gradient(
    var(--chk-ink,var(--royale)) 0 25%,
    transparent 0 50%,
    var(--chk-ink,var(--royale)) 0 75%,
    transparent 0);
  background-size:calc(var(--chk-cell) * 2) calc(var(--chk-cell) * 2);
  background-position:0 0;
}
/* hero: anchored left, sitting on the join into Roy's band */
/* one row lands below the join, two sit over the photograph above it */
/* --edge is where the CONTENT starts, not where the viewport does: past the
   wrap's 1240px max the gutter stops being the left margin, and a strip pinned
   to the viewport drifts out of line with the wordmark above it. */
/* Both strips run off the screen edge — left at the top, right at the bottom.
   They were anchored to the content line, which read as a strip that had
   stopped short rather than one that carries on past the frame. */
.hero .checker{
  bottom:calc(var(--chk-cell) * -1);
  left:0;
  /* NEVER a cut square. The width is fluid, so its right edge would otherwise
     land mid-square at most viewport sizes. round(down, …, --chk-cell) snaps
     the box to a whole number of squares; the first declaration is the
     fallback for engines without round(), capped at an exact multiple. */
  width:min(46%,576px);
  width:round(down, min(46%,576px), var(--chk-cell));
}
/* footer: anchored right, so the page counterweights top-left / bottom-right */
.footer .checker{
  /* This one crosses the red band into the dark footer. Red squares on a red
     ground disappear, so the strip is INVERTED here: cream squares, gaps left
     open. The pattern then stays identical the whole way down and only the
     ground behind it changes at the join — which is the point. */
  --chk-ink:var(--cream);
  --chk-light:transparent;
  top:calc(var(--chk-cell) * -2);
  right:0;
  width:min(44%,540px);
  width:round(down, min(44%,540px), var(--chk-cell));
}
.footer{position:relative}
/* the cell is fluid now, so these only widen the strips on small screens */
@media (max-width:720px){
  .hero .checker{width:min(78%,520px);width:round(down,min(78%,520px),var(--chk-cell))}
  .footer .checker{width:min(74%,480px);width:round(down,min(74%,480px),var(--chk-cell))}
}

/* --------------------------------------------------------------------------
   4. BUTTONS
   -------------------------------------------------------------------------- */
.btn{
  display:inline-flex;align-items:center;flex-wrap:nowrap;gap:.8rem;
  font-family:var(--display);font-size:.82rem;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;
  padding:.78rem 1.05rem .78rem 1.5rem;
  border:1px solid var(--accent);border-radius:100px;
  color:var(--fg);
  position:relative;cursor:pointer;
  transition:background-position .5s var(--ease),color .4s var(--ease),border-color .4s var(--ease);
  white-space:nowrap;
}
/* The wipe is a BACKGROUND LAYER on the button, not a pseudo-element.
   As a clipped child it sat exactly on the rounded overflow boundary, and at
   high DPR the antialiasing there let a hairline of the button's own colour
   show along the bottom edge for the whole half-second of the animation.
   A background-image is clipped by the same radius as the background-colour
   underneath it, so there is no second edge to leak through.
   Two stops, no midpoint: the front of the wipe stays a straight edge. */
.btn{
  background-color:var(--ground,transparent);
  background-image:linear-gradient(to right, var(--wipe,var(--accent)) 0 50%, transparent 50%);
  background-repeat:no-repeat;
  /* 202%, not 200%. At exactly 200% the gradient's hard stop lands ON the left
     edge of the positioning area, and AT DPR 1 it rounds onto the first device
     pixel — one column of the wipe colour showing inside the pill, which is
     the hairline you see on the red buttons. It does not appear at 2x or 4x,
     so it has to be measured at 1x. The extra 2% parks the stop just off
     canvas at both ends: hidden at rest, fully past the right edge on hover. */
  background-size:202% 100%;
  background-position:100% 0;
}
.btn:hover,.btn:focus-visible{
  background-position:0 0;
  color:var(--cream);
  /* The label recolours 160ms LATE. Both the ground and the text were
     transitioning from the moment the pointer landed, and on the red band the
     resting ground is cream and the hover label is cream — so for the first
     fraction of a second the button was cream text on a cream ground and the
     label vanished. Letting the wipe get a head start means the dark ground is
     already under the text by the time the text changes colour. No delay on
     the way out, where the ground retreats from the same edge the text sits. */
  transition:background-position .5s var(--ease),
             color .3s var(--ease) .16s,
             border-color .4s var(--ease);
}
.btn__arrow{
  width:1.65rem;height:1.65rem;flex:0 0 auto;
  border:1px solid currentColor;border-radius:50%;
  display:grid;place-items:center;
  transition:transform .45s var(--ease);
}
.btn__arrow svg{width:13px;height:10px;display:block}
.btn:hover .btn__arrow{transform:translateX(3px)}

.btn--solid{--ground:var(--royale);--wipe:var(--char);border-color:var(--royale);color:var(--cream)}
.btn--solid:hover,.btn--solid:focus-visible{color:var(--cream);border-color:var(--char)}
.theme-char .btn--solid{--ground:var(--royale);--wipe:var(--cream);border-color:var(--royale);color:var(--cream)}
.theme-char .btn--solid:hover{color:var(--char);border-color:var(--cream)}
.theme-red .btn--solid{--ground:var(--cream);--wipe:var(--char);border-color:var(--cream);color:var(--royale-deep)}
.theme-red .btn--solid:hover{color:var(--cream);border-color:var(--char)}
.btn--small{font-size:.74rem;padding:.58rem .8rem .58rem 1.15rem;gap:.65rem}
.btn--small .btn__arrow{width:1.4rem;height:1.4rem}
/* The outline button on a DARK band. Its wipe defaults to --accent, and on
   two themes that accent is lighter than the label the button hovers to:
   theme-red wipes cream and the label turns cream, so it vanished into its own
   ground; theme-char wipes sand, which left cream on tan at about 1.2:1.
   Both take an ink label on hover instead — the pairing .btn--solid already
   uses at rest. :not() so the solid button keeps its own treatment. */
.theme-red .btn:not(.btn--solid):hover,
.theme-red .btn:not(.btn--solid):focus-visible,
.theme-char .btn:not(.btn--solid):hover,
.theme-char .btn:not(.btn--solid):focus-visible{color:var(--char)}
/* Freeform writes the submit button itself and its label is plain text, so the
   circled arrow every other button carries has to be drawn rather than marked
   up. Two pseudos: ::after is the disc, ::before is the glyph, masked so it
   still takes currentColor and works on both the red and the outline button.
   A single pseudo cannot do it — a mask applies to the border too, so the disc
   would be masked away with it. */
.btn--formed{position:relative;padding-right:3.1rem}
.btn--formed::after{
  content:"";position:absolute;right:.8rem;top:50%;translate:0 -50%;
  width:1.65rem;height:1.65rem;
  border:1px solid currentColor;border-radius:50%;
  transition:transform .45s var(--ease);
}
.btn--formed::before{
  content:"";position:absolute;right:1.06rem;top:50%;translate:0 -50%;
  width:13px;height:10px;background:currentColor;z-index:1;
  -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 10'%3E%3Cpath d='M8 1l4 4-4 4M12 5H0' stroke='%23000' stroke-width='1.2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px 10px no-repeat;
          mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 13 10'%3E%3Cpath d='M8 1l4 4-4 4M12 5H0' stroke='%23000' stroke-width='1.2' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 13px 10px no-repeat;
  transition:transform .45s var(--ease);
}
.btn--formed:hover::after,.btn--formed:hover::before{transform:translateX(3px)}

/* a bare text link with a rule that draws on hover */
.text-link{
  align-self:flex-start;
  font-family:var(--display);font-size:.8rem;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;color:var(--accent);
  display:inline-flex;align-items:center;gap:.6rem;position:relative;padding-bottom:.35rem;
}
.text-link::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:currentColor;transform:scaleX(1);transform-origin:left;
  transition:transform .4s var(--ease);
}
.text-link:hover::after{transform:scaleX(.4)}
.text-link svg{width:13px;height:10px}

/* --------------------------------------------------------------------------
   5. HEADER
   -------------------------------------------------------------------------- */
/* This measured 9.94:1 — contrast was never the problem. It was 12.5px at the
   body weight of 350, which is simply too small and too light to read
   comfortably. Size and weight up, alpha off the colour entirely. */
.utility{background:var(--char);color:var(--sand);font-size:.86rem;font-weight:400;letter-spacing:.01em}
/* The alert state. --royale-deep, not --royale: cream on the vivid red is
   2.89:1 and fails at 13.8px, while cream on #c4301d measures 4.71:1 and
   passes. It also keeps the strip off the same red as the BOOK A TEE TIME
   button sitting 40px below it, which otherwise read as one big red smear.
   Red is never the resting state — the bar going red is the signal. */
.utility--alert{background:var(--royale-deep);color:var(--cream)}
.utility--alert .open{color:var(--cream);font-weight:600}
.utility--alert a{color:var(--cream)}
.utility--alert a:hover{color:var(--sand)}
/* only the announcement's own link is underlined; the phone number is not */
.utility__more{text-decoration:underline;text-underline-offset:2px}
.utility .wrap{display:flex;flex-wrap:wrap;gap:.2rem 1.6rem;align-items:center;padding-block:.5rem}
/* min-width:0, or a long announcement refuses to wrap: a flex item will not
   shrink below min-content while min-width is auto, so the sentence ran off
   the right edge on a phone and took the link and the phone number with it. */
.utility .wrap > *{min-width:0}
.utility .open{color:var(--cream);font-weight:700}
.utility .spacer{margin-left:auto}
.utility a:hover{color:var(--cream)}
@media (max-width:767px){
  .utility .hide-sm{display:none}
  /* On a phone an announcement stops being a flex row and becomes a centred
     block. Flex was refusing to shrink the sentence — it ran off the right
     edge and took the link and the phone with it — and block layout wraps
     text without having to win that argument. It also reads better: a
     statement centred over its own lines rather than split left and right. */
  .utility .wrap:has(.utility__more){display:block;text-align:center}
  .utility .wrap:has(.utility__more) .open{display:block}
  .utility .wrap:has(.utility__more) .spacer{display:none}
  .utility .wrap:has(.utility__more) .utility__more,
  .utility .wrap:has(.utility__more) [href^="tel:"]{display:inline-block;margin:.2rem .55rem 0}
}

.header{position:sticky;top:0;z-index:60;background:var(--cream);border-bottom:1px solid rgba(54,55,24,.12)}
.header__inner{display:flex;align-items:center;justify-content:space-between;gap:1.5rem;padding-block:1rem}
.logo img{height:clamp(46px,4.4vw,62px);width:auto}

/* Flat nav, six items, no dropdowns. A nine-hole course with a mini golf,
   a range and a kitchen does not need a mega menu. */
.nav{display:none;align-items:center;gap:.1rem;list-style:none;margin:0;padding:0}
@media (min-width:1000px){.nav{display:flex}}
  /* :not(.btn) — .nav a would otherwise out-specify .btn and kill its
     display:inline-flex, stacking the label above the arrow. */
/* > li > a — direct children only. Unscoped, this matched the SUBNAV links
   too (they are inside .nav), handing them the slab face and the sliding
   underline that belong to the top level. */
.nav > li > a:not(.btn){
  font-family:var(--display);font-size:.84rem;font-weight:400;
  letter-spacing:.16em;text-transform:uppercase;
  color:var(--pine);padding:.6rem .7rem;display:block;position:relative;
  transition:color .3s;
}
.nav > li > a:not(.btn)::after{
  content:"";position:absolute;left:.7rem;right:.7rem;bottom:.3rem;height:1px;
  background:var(--royale);transform:scaleX(0);transform-origin:left;
  transition:transform .4s var(--ease);
}
.nav > li > a:not(.btn):hover::after{transform:scaleX(1)}
.nav .btn{margin-left:1.25rem;flex:none}
.nav .btn::after{display:none}

.burger{background:none;border:0;color:var(--pine);padding:.5rem;cursor:pointer;display:grid;place-items:center}
@media (min-width:1000px){.burger{display:none}}
.burger span{display:block;width:20px;height:1px;background:currentColor;box-shadow:0 -6px 0 currentColor,0 6px 0 currentColor}


/* --------------------------------------------------------------------------
   6. HERO
   -------------------------------------------------------------------------- */
/* A 3:2 frame in a 3:1 box crops to a thin middle band and throws away the
   sky, which in a dusk shot is the asset. Give the hero real height so the
   photograph keeps its composition — sized to content, not to 100vh. */
.hero{
  position:relative;background:var(--char);color:var(--cream);
  min-height:clamp(540px,max(70vh,33vw),900px);
  display:grid;align-items:center;
}
.hero__bg{position:absolute;inset:0}
.hero__bg img{width:100%;height:100%;object-fit:cover;object-position:50% 52%}
.hero__bg::after{
  content:"";position:absolute;inset:0;
  background:
    /* the veil has to outrun the claim row — it ends near 80% of the
       viewport, and type past the fade loses its ground */
    linear-gradient(94deg,
      rgba(242,237,215,.95) 0%,
      rgba(242,237,215,.9) 30%,
      rgba(242,237,215,.74) 52%,
      rgba(242,237,215,.4) 68%,
      rgba(242,237,215,.08) 80%,
      rgba(242,237,215,0) 88%),
    linear-gradient(180deg, rgba(242,237,215,.5) 0%, rgba(242,237,215,0) 18%);
}
/* width:100% — .hero is display:grid for vertical centring, and its single
   implicit track was sizing to the content rather than stretching, which
   pushed the whole lockup ~150px in from the gutter. The wordmark, the claim
   rules and the checkerboard now all start on the same left line as the
   header logo. */
/* Asymmetric padding, not less height. The hero is display:grid with
   align-items:center and a min-height, so the height is set by min-height and
   the padding only decides where the content sits inside it. Making the top
   padding smaller than the bottom lifts the block toward the header without
   the band losing a pixel. */
.hero .wrap{position:relative;width:100%;padding-block:clamp(1.5rem,3vw,2.75rem) clamp(4rem,8vw,7.5rem)}
/* A percentage here starved the lockup: the two auto grid tracks could not
   fit the mark plus the trade tier, so both shrank and the wordmark collapsed
   to a third of its size. Fixed measure, and the photo keeps the right. */
.hero__copy{max-width:49rem}

.hero .intro > :where(span,h1,h2,p){color:var(--royale-ink)}
.hero .headline{color:var(--char)}
.hero .note{font-size:clamp(1.02rem,1.25vw,1.18rem);max-width:44ch;margin-top:1.5rem}
.hero .note p{color:#5f5632}
.hero__actions{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:2rem}
.hero .btn{color:var(--char);border-color:rgba(36,29,18,.45)}
.hero .btn{--wipe:var(--char)}
.hero .btn:hover{color:var(--cream);border-color:var(--char)}
.hero .btn--solid{--ground:var(--royale);--wipe:var(--char);border-color:var(--royale);color:var(--cream)}
.hero .btn--solid:hover{color:var(--cream);border-color:var(--char)}

/* --------------------------------------------------------------------------
   7. ROY & PICK YOUR GAME
   -------------------------------------------------------------------------- */
/* Roy gets his own band. He is the only place the script speaks. */
.says__grid{--bs-gutter-x:clamp(1.5rem,4vw,3.5rem)}
/* an explicit track, not auto: an SVG carrying only a viewBox has an
   intrinsic width of 300px, so an auto track sized to THAT and quietly
   ignored the max-width on the element */

/* Roy's flag crosses the checkerboard above him rather than sliding under it.
   The z-index has to go on the BAND'S WRAP, not on Roy: .band .wrap carries
   z-index:1, which makes it a stacking context, so any z-index on a child is
   resolved inside that context and the whole thing still paints below the
   checker's 4. Raising the context itself is the fix. */
/* .band.band--roy — two classes, so it out-specifies .band .wrap rather than
   relying on source order. The .band .wrap{z-index:1} rule sits 600 lines
   later in the file and was silently winning the tie. */
.band.band--roy > .wrap{z-index:6}
.roy{margin:0;text-align:center;position:relative}
.roy svg{width:100%;height:auto;max-width:clamp(250px,30vw,400px);margin-inline:auto;overflow:visible}
.roy figcaption{
  margin-top:1rem;
  font-family:var(--script);font-weight:400;
  font-size:1.9rem;line-height:1;letter-spacing:.01em;
  color:var(--fg-soft);
}

.says__quote > span{
  display:block;
  font-family:var(--display);font-size:.74rem;font-weight:500;
  letter-spacing:.24em;text-transform:uppercase;color:var(--accent);
  margin-bottom:1.25rem;
}
.says__quote p{
  font-family:var(--script);font-weight:400;
  font-size:clamp(2.1rem,4.6vw,3.6rem);
  line-height:1.18;letter-spacing:.01em;   /* the script has deep descenders */
  color:var(--royale);
  max-width:15ch;margin:0;
}
.says__quote small{
  display:block;margin-top:1.9rem;
  font-family:var(--display);font-size:.74rem;font-weight:500;
  letter-spacing:.24em;text-transform:uppercase;color:var(--fg-soft);
}

.games{display:grid;gap:1px;background:var(--hair);border:1px solid var(--hair);grid-template-columns:1fr;margin-top:clamp(2rem,3.5vw,3rem)}
@media (min-width:620px){.games{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1040px){.games{grid-template-columns:repeat(4,1fr)}}
.game{background:var(--bg);padding:1.6rem 1.5rem 1.5rem;display:flex;flex-direction:column;transition:background .35s var(--ease)}
.game:hover{background:rgba(196,48,29,.05)}
.theme-red .game:hover{background:rgba(242,237,215,.1)}
.game h3{margin-bottom:.35rem}
.game > span{
  font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--accent);font-variant-numeric:tabular-nums;
}
.game p{font-size:.98rem;font-weight:400;line-height:1.6;color:var(--fg-soft);max-width:32ch;margin:.5rem 0 1.4rem}
.theme-red .game p,.theme-red .note p{font-weight:400}
.game .text-link{margin-top:auto}

/* --------------------------------------------------------------------------
   8. SPLIT — after dark, eat, membership, groups
   -------------------------------------------------------------------------- */
/* ---- Bootstrap grid ----
   The page-level two-column layouts are Bootstrap rows and columns, not
   bespoke CSS Grid. Component-internal layouts (the card row, the plate
   grid, the scorecard) stay CSS Grid, because they use a 1px gap over a
   coloured parent to draw their hairlines and Bootstrap's negative-margin
   gutters cannot do that.

   Gutters are set with --bs-gutter-x, never column-gap on a .row. */
.band .row{--bs-gutter-x:clamp(2rem,4.5vw,4rem);--bs-gutter-y:2.25rem}
.band .row > [class*="col-"]{display:flex;flex-direction:column;justify-content:center}

.split__body .intro{margin-bottom:0}
.band .row figure img:not(.sticker){width:100%;aspect-ratio:4/3;object-fit:cover}

.specs{list-style:none;margin:1.9rem 0 2rem;padding:0}
.specs li{display:flex;flex-wrap:wrap;gap:.1rem 1.25rem;padding:.8rem 0;border-top:1px solid var(--hair)}
.specs li:last-child{border-bottom:1px solid var(--hair)}
.specs b{flex:none;width:7rem;font-family:var(--display);font-weight:500;font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);padding-top:.32rem}
.specs span{flex:1 1 15rem;font-weight:400;color:var(--fg-soft)}

/* --------------------------------------------------------------------------
   9. EAT
   -------------------------------------------------------------------------- */
.plates{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.plates img{width:100%;aspect-ratio:1/1;object-fit:cover}
.plates img:first-child{aspect-ratio:1/1.34;grid-row:span 2}
@media (max-width:479px){
  .plates img:first-child{grid-row:auto;grid-column:span 2;aspect-ratio:3/2}
}

/* --------------------------------------------------------------------------
   10. MEMBERSHIP
   -------------------------------------------------------------------------- */
.member{background:var(--char);color:var(--cream);padding:clamp(1.75rem,3vw,2.5rem)}
.member h3{color:var(--cream);margin-bottom:.6rem}
.member .tbc{margin-bottom:1.4rem;
  display:inline-block;font-family:var(--display);font-size:.7rem;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--sand);
  border:1px dashed currentColor;padding:.28rem .55rem;
}
.member .btn{margin-top:1.75rem;--ground:var(--royale);--wipe:var(--cream);border-color:var(--royale);color:var(--cream)}
.member .btn:hover{color:var(--char);border-color:var(--cream)}
.benefits{list-style:none;margin:1.9rem 0 0;padding:0;max-width:34rem}
.benefits li{padding:.68rem 0;border-top:1px solid var(--hair);font-weight:400;color:var(--fg-soft)}
.benefits li:last-child{border-bottom:1px solid var(--hair)}

/* --------------------------------------------------------------------------
   11. EVENTS
   -------------------------------------------------------------------------- */
.stats{display:grid;gap:1px;background:var(--hair);border:1px solid var(--hair);grid-template-columns:repeat(3,1fr);margin-top:2rem}
.stat{background:var(--bg);padding:1.25rem 1.2rem}
.stat b{display:block;font-family:var(--poster);font-size:1.6rem;line-height:1;color:var(--accent)}
.stat span{display:block;font-size:.88rem;font-weight:400;color:var(--fg-soft);margin-top:.4rem}

.diary{list-style:none;margin:clamp(2rem,3.5vw,3rem) 0 0;padding:0;border-top:1px solid var(--hair)}
.diary li{border-bottom:1px solid var(--hair)}
.diary a,.diary__row{display:grid;gap:.1rem 1.5rem;grid-template-columns:1fr;padding:1.2rem 0;align-items:baseline;transition:color .3s}
@media (min-width:720px){.diary a{grid-template-columns:7rem 1fr auto}.diary__row{grid-template-columns:7rem 1fr}}
.diary a > span:first-child,.diary__row > span:first-child{font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--accent)}
.diary h3{margin:0;font-size:1.22rem}
.diary p{margin:.2rem 0 0;font-size:.96rem;font-weight:400;color:var(--fg-soft);max-width:54ch}
.diary a:hover h3{color:var(--accent)}
.diary svg{width:13px;height:10px;color:var(--accent)}

/* --------------------------------------------------------------------------
   12. VISIT
   -------------------------------------------------------------------------- */
.nap{font-style:normal;margin:1.6rem 0 2rem}
.nap .addr{font-family:var(--display);font-weight:400;font-size:1.45rem;line-height:1.3;margin-bottom:.6rem;color:var(--fg)}
.hours-wrap{display:grid;gap:1.75rem;grid-template-columns:1fr}
@media (min-width:520px){.hours-wrap{grid-template-columns:1fr 1fr}}
.hours h4{font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--accent);margin:0 0 .75rem}
.hours dl{margin:0}
.hours__row{display:flex;justify-content:space-between;gap:1rem;padding:.48rem 0;border-top:1px solid var(--hair);font-size:.98rem}
.hours dt{color:var(--fg-soft);margin:0;font-weight:400}
.hours dd{margin:0;font-variant-numeric:tabular-nums;white-space:nowrap}
.hours__row--closed dd{color:var(--fg-soft);font-weight:400}
.mapbox{position:relative;aspect-ratio:4/3;background:var(--sand);border:1px solid var(--hair);display:grid;place-items:center;text-align:center;padding:1.5rem}
.mapbox p{color:#5f5632;font-size:.9rem;font-weight:400;max-width:30ch}

/* --------------------------------------------------------------------------
   13. FOOTER
   -------------------------------------------------------------------------- */
/* 7–10:1 everywhere, so contrast was never the issue — it was weight 300 at
   15px, the same thing that made the utility bar hard to read. Weight up,
   alpha up, size up a touch. */
.footer{background:var(--char);color:rgba(242,237,215,.86)}
.footer a:hover{color:var(--cream)}
.footer__top{--bs-gutter-x:2.5rem;--bs-gutter-y:2.5rem;padding-block:clamp(3rem,5vw,4.5rem)}
.footer__brand img{height:64px;width:auto;margin-bottom:1.25rem}
.footer__brand p:not(.foot-script){font-weight:400;font-size:1rem;line-height:1.65;max-width:30ch;color:rgba(242,237,215,.8)}
.newsletter label{display:block;font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--sand);margin-bottom:.6rem}
/* Freeform wraps each input in its own container and injects the submit
   button before </form>, outside the row — so the input and the button are
   never siblings, which is what this flex row assumed. Making the FORM the
   row and letting the row box vanish with display:contents puts them back on
   one line without asking the plugin to change its markup. */
.newsletter{display:flex;flex-wrap:wrap;align-items:flex-end;gap:.5rem}
.newsletter .field{display:contents}
.newsletter__field{flex:1 1 12rem;min-width:0}
.newsletter__field input{width:100%}
/* Freeform's scroll anchor is an empty div and would otherwise sit in the
   flex row collecting a gap of its own */
.newsletter > [data-scroll-anchor]{display:none}
/* The banner Freeform's JS injects on an AJAX submit. It arrives under the
   plugin's own class names, not the ones renderTag sets, so it is dressed
   here to match the footer rather than landing as unstyled body text. */
.newsletter .freeform-form-success,
.newsletter .freeform-form-errors,
.newsletter__note{
  flex:1 1 100%;margin:0 0 .15rem;font-size:.92rem;line-height:1.45;
}
.newsletter .freeform-form-success,
.newsletter__note{color:var(--sand)}
.newsletter .freeform-form-errors,
.newsletter__note--bad{color:#ff9a8c}
.newsletter .freeform-form-success p,
.newsletter .freeform-form-errors p{margin:0}
.newsletter .freeform-form-errors ul{list-style:none;margin:.3rem 0 0;padding:0}
/* a label the design does not show, but a screen reader still needs */
.visually-hidden{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0;
}
.newsletter input{
  flex:1 1 12rem;min-width:0;
  background:rgba(242,237,215,.06);
  border:1px solid rgba(242,237,215,.3);
  color:var(--cream);
  padding:.92rem 1rem;border-radius:var(--radius);
  font:inherit;font-size:1rem;font-weight:400;
  transition:border-color .3s var(--ease),background .3s var(--ease);
}
.newsletter input::placeholder{color:rgba(242,237,215,.45)}
/* focus brightens the field; the ring itself is the global focus outline, so
   there is one treatment rather than a red border AND a red ring fighting */
.newsletter input:focus{
  background:rgba(242,237,215,.11);
  border-color:rgba(242,237,215,.62);
}
.footer :where(input,button,a):focus-visible{outline-color:var(--royale)}
/* solid red, so the block has one thing that is unmistakably the action */
.newsletter .btn{--ground:var(--royale);--wipe:var(--cream);border-color:var(--royale);color:var(--cream)}
.newsletter .btn:hover{color:var(--char);border-color:var(--cream)}
.footer__nav{display:grid;gap:1.75rem;grid-template-columns:repeat(2,1fr)}
@media (min-width:600px){.footer__nav{grid-template-columns:repeat(3,1fr)}}
.footer__nav h4{font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--sand);margin:0 0 .85rem}
.footer__nav ul{list-style:none;margin:0;padding:0;font-size:1rem;font-weight:400}
.footer__nav li{margin-bottom:.45rem}
.footer__bottom{border-top:1px solid rgba(242,237,215,.16);padding-block:1.4rem 2rem;display:flex;flex-wrap:wrap;gap:.5rem 1.5rem;align-items:center;font-size:.9rem;font-weight:400;color:rgba(242,237,215,.7)}
.footer__bottom .spacer{margin-left:auto}
.social{display:flex;gap:.6rem}
/* Red discs with real marks, not initials in a hairline ring — they were
   invisible on the dark footer and "IG"/"FB" is a placeholder, not a brand. */
.social a{
  width:42px;height:42px;border-radius:50%;
  background:var(--royale);border:0;
  display:grid;place-items:center;color:var(--cream);
  transition:transform .25s var(--ease),background .25s var(--ease);
}
.social a svg{width:20px;height:20px;display:block}
.social a:hover{background:var(--cream);color:var(--royale-deep);transform:translateY(-2px)}

/* mobile book bar */
.bookbar{position:fixed;left:0;right:0;bottom:0;z-index:70;background:var(--char);display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.7rem var(--gutter)}
.bookbar span{color:rgba(242,237,215,.8);font-family:var(--display);font-size:.8rem;letter-spacing:.06em}
.bookbar .btn{--ground:var(--royale);--wipe:var(--cream);border-color:var(--royale);color:var(--cream)}
.bookbar .btn:hover{color:var(--char)}
@media (min-width:1000px){.bookbar{display:none}}
@media (max-width:999px){.footer__bottom{padding-bottom:5rem}}

/* --------------------------------------------------------------------------
   14. MOTION
   -------------------------------------------------------------------------- */
.rv{opacity:1;transform:translateY(14px);transition:transform .8s var(--ease)}
.rv.in{transform:none}
@media (prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}
  .rv{transform:none}
  html{scroll-behavior:auto}
}


/* ==========================================================================
   16. CHARACTER DEVICES
   Three, and only three. The page was structurally sound but generic — a
   clean editorial layout that could have belonged to any brand. These are
   the things only a golf course in Callahan gets to use.
   ========================================================================== */

/* --- 16a. The badge. The brand guide's circular lockup, rebuilt from the
   icon so it can carry the location. It is the only element that states
   where you are without being a sentence. --------------------------------- */
.badge{width:100%;height:auto;display:block;color:currentColor}
/* The seal is the real artwork (badge2.svg from the guide), not a
   reconstruction. Its classes were generic .st0/.st1 — namespaced to .bs-*
   so they cannot collide with any other inlined SVG, exactly as Roy's were.
   Recoloured here rather than in the file so the disc can change ground. */
.badge .bs-st1{fill:var(--sand)}      /* lettering, inner ring, monogram */
.badge .bs-st0{fill:var(--royale-ink)} /* the hairline outer ring */
/* The stamp straddles the seam — half on the photograph, half on the band
   below, centred on the checkerboard. The checkerboard is ALSO Royale Red, so
   a red disc laid over it would simply merge: the cream ring is what punches
   it out of both grounds, and it reads as a seal applied across a join. */
.badge-stamp{
  position:absolute;right:calc(var(--gutter) + 8px);bottom:-92px;z-index:5;
  width:clamp(150px,15vw,196px);color:var(--sand);
  background:var(--royale);border-radius:50%;padding:15px;
  box-shadow:0 0 0 6px var(--cream), 0 14px 30px rgba(36,29,18,.34);
}
/* below ~1100 the stamp starts sitting on top of Roy's quote */
@media (max-width:1099px){.badge-stamp{display:none}}

/* ---- HIDDEN FOR NOW ----------------------------------------------------
   Parked, not removed. The markup, the real seal artwork (badge-seal.svg,
   inlined) and all the positioning above are intact — delete this one rule
   to bring it back exactly as it was, straddling the checkerboard seam. */
.badge-stamp{display:none}

/* --- 16b. The scorecard. A golf course's own document — ruled, boxed,
   tabular, read across. The rates live in it because the rates ARE a grid,
   and because a flat price list is exactly the generic thing this page had
   too much of. ------------------------------------------------------------ */
.card-wrap{margin-top:clamp(1.8rem,3vw,2.6rem)}
.scorecard{
  border-collapse:separate;border-spacing:0;width:100%;min-width:38rem;
  font-family:var(--display);font-variant-numeric:tabular-nums;
  background:var(--bg);
}
.scorecard caption{
  caption-side:top;text-align:left;padding:0 0 .9rem;
  font-family:var(--display);font-size:.74rem;font-weight:500;
  letter-spacing:.24em;text-transform:uppercase;color:var(--accent);
}
.scorecard th,.scorecard td{
  border-right:1px solid var(--hair);border-bottom:1px solid var(--hair);
  padding:.62rem .5rem;text-align:center;font-weight:400;font-size:1rem;
}
.scorecard tr > *:last-child{border-right:0}
.scorecard tbody tr:last-child > *{border-bottom:0}
.scorecard thead th{
  /* --char, not --fg: --fg is the olive ink, and an olive bar sitting above
     a column of red prices is the Christmas problem again. */
  background:var(--char);color:var(--cream);
  font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;font-weight:500;
  border-right-color:rgba(242,237,215,.25);
}
.scorecard th[scope="row"]{
  text-align:left;padding-left:.9rem;font-size:.86rem;
  letter-spacing:.02em;color:var(--fg);white-space:nowrap;
}
.scorecard td{color:var(--fg-soft)}
.scorecard .is-best{color:var(--accent);font-weight:500}
.scorecard .is-na{color:var(--hair)}
.scorecard-note{
  background:var(--shell);font-size:.86rem;color:var(--fg-soft);
  text-align:left;padding:.7rem .9rem;letter-spacing:.01em;
  margin:0;border-top:1px solid var(--hair);
}
.theme-sand .scorecard-note{background:rgba(255,255,255,.28)}
.card-note{margin-top:.9rem;font-size:.9rem;font-weight:400;color:var(--fg-soft)}

/* --- 16c. Checkerboard as structure, not confetti. It closes a band rather
   than decorating one, so it always means "a section ended here". --------- */

/* --- FAQ. Real questions, real answers, and an FAQPage schema block in the
   head. Half of local search for a venue like this is people asking a
   question a machine has to be able to read. ------------------------------ */
.faq{border-top:1px solid var(--hair);margin-top:clamp(1.8rem,3vw,2.6rem)}
.faq details{border-bottom:1px solid var(--hair)}
.faq summary{
  list-style:none;cursor:pointer;padding:1.15rem 2.5rem 1.15rem 0;position:relative;
  font-family:var(--display);font-size:1.12rem;font-weight:400;color:var(--fg);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{
  content:"";position:absolute;right:.35rem;top:50%;width:11px;height:11px;
  border-right:1.5px solid var(--accent);border-bottom:1.5px solid var(--accent);
  transform:translateY(-70%) rotate(45deg);transition:transform .3s var(--ease);
}
.faq details[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.faq summary:hover{color:var(--accent)}
.faq .answer{padding:0 0 1.3rem}
.faq .answer p{font-weight:400;color:var(--fg-soft);max-width:60ch}

/* ==========================================================================
   17. POSTER DEVICES
   From the reference set: Patty's Drive-In (Anton statements, the claim row,
   the checkerboard field), the Pink Feather menus (the double-ruled frame),
   Green Parades (the mascot patch, the badge shapes).
   ========================================================================== */

/* --- 17a. The claim row. Patty's runs three short claims in small caps
   across the foot of its poster — COLD DRINKS GOOD TIMES / DRIVE-IN TIME /
   BURGERS THAT HIT DIFFERENT. It is the cheapest character on the page and
   it carries real information. -------------------------------------------- */
.claims{
  display:grid;grid-template-columns:1fr;
  border-top:1px solid var(--hair);
  margin-top:clamp(2rem,4vw,3rem);
}
@media (min-width:640px){.claims{grid-template-columns:repeat(3,1fr)}}
.claims div{
  padding:1.05rem 1.2rem 1.05rem 0;
  border-bottom:1px solid var(--hair);
}
@media (min-width:640px){
  .claims div{border-bottom:0;border-right:1px solid var(--hair);padding-left:1.2rem}
  .claims div:first-child{padding-left:0}
  .claims div:last-child{border-right:0}
}
.claims b{
  display:block;font-family:var(--poster);text-transform:uppercase;
  font-size:1.12rem;line-height:1.1;letter-spacing:.005em;color:var(--fg);
}
.claims span{
  display:block;margin-top:.35rem;
  font-family:var(--display);font-size:.7rem;font-weight:500;
  letter-spacing:.13em;text-transform:uppercase;color:var(--fg-soft);
}
.hero .claims{border-top-color:rgba(36,29,18,.28)}
.hero .claims div{border-color:rgba(36,29,18,.28)}
.hero .claims b{color:var(--char)}
.hero .claims span{color:#5f5632}

/* --- 17b. The double-ruled frame. Straight off the speakeasy menus — an
   outer rule, a gap, an inner rule. It says "this is a printed document",
   which is exactly what a scorecard and a menu are. --------------------- */
.frame{background:var(--bg)}
.frame > *{border:0}
.scorecard{border:0}

/* --- 17c. Checkerboard as a FIELD, not a hairline. At 20px it was a rule
   nobody noticed; at 44px it is the brand's own texture doing work. ------- */

/* --- 17d. (removed) The checker wash behind Roy's band is gone. A texture
   tiled across a whole field stops being a device and becomes wallpaper —
   the checkerboard earns more by being used sparingly, at an edge. ------- */

/* --- 17e. Statements sit on a rule, poster-style. -------------------- */
.intro > span{margin-bottom:1rem}
.band-rule{
  display:block;width:2.6rem;height:3px;background:var(--royale);
  margin:0 0 1.5rem;
}
.hero .band-rule{background:var(--royale)}
.theme-red .band-rule{background:var(--royale)}
.theme-char .band-rule{background:var(--royale)}

/* ==========================================================================
   18. THE SIGN
   From Helms Workshop's Frank. Their strategy line — "take a traditionally
   ordinary food and transform it into an extraordinary experience" — is the
   whole trick, and the identity delivers it with a painted wall sign built
   in three tiers:

        Frank                          ornate script, offset shadow
        HOT DOGS · COLD BEER           heavy condensed caps
        PURVEYORS OF ARTISAN SAUSAGE   light, widely spaced caps

   Pine Royale has tier one and has never had tiers two or three. The brand
   guide's badge already writes them: "PINE ROYALE · CALLAHAN FLORIDA ·
   9 HOLES & MINIGOLF".
   ========================================================================== */
.sign{text-align:center}
.sign__mark{
  width:clamp(230px,34vw,440px);margin:0 auto;
  filter:drop-shadow(7px 7px 0 var(--royale));
}
.sign__trade{
  font-family:var(--poster);text-transform:uppercase;
  font-size:clamp(1.02rem,2.1vw,1.7rem);line-height:1.3;letter-spacing:.015em;
  color:var(--cream);margin:2.2rem 0 0;
}
.sign__desc{
  font-family:var(--display);font-weight:500;
  font-size:clamp(.66rem,1.15vw,.8rem);letter-spacing:.34em;text-transform:uppercase;
  color:var(--sand);margin:1.05rem 0 0;
}
.sign__rule{
  display:block;width:min(560px,80%);height:1px;background:rgba(242,237,215,.3);
  margin:1.5rem auto;border:0;
}
.sign__place{
  font-family:var(--display);font-weight:500;
  font-size:.74rem;letter-spacing:.3em;text-transform:uppercase;
  color:rgba(242,237,215,.6);margin:0;
}

/* --- The dashed circle. Frank's coasters ring the monogram with a dashed
   rule — it reads as "stamped", and it is the one line on the page that
   isn't solid. ------------------------------------------------------------ */


/* --- The phone number, unframed.
   It had been in a double-ruled plaque with a row of stars — the Frank device
   scaled down. At plaque size the frame and the stars were doing more talking
   than the number, so both came off. What is left is the thing that matters:
   the number, large, in the poster face, in red. It is still the highest-intent
   element on a local page and it still reads as the loudest thing in the band,
   without any furniture around it. ------------------------------------------ */
.callout{margin:0 0 2rem}
.callout__label{
  font-family:var(--display);font-weight:500;font-size:.72rem;
  letter-spacing:.24em;text-transform:uppercase;color:var(--fg-soft);margin:0 0 .5rem;
}
.callout__num{
  display:inline-block;
  font-family:var(--poster);font-weight:400;
  font-size:clamp(1.9rem,3.4vw,2.7rem);line-height:1;letter-spacing:.005em;
  color:var(--royale-ink);
  transition:color .3s var(--ease);
}
.callout__num:hover{color:var(--char)}
.callout__sub{
  font-family:var(--display);font-weight:500;font-size:.74rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--fg-soft);margin:.7rem 0 0;
}

/* --- 18b. The sign, above the fold. The closing sign band was the best
   moment on the page and it was the last thing anyone saw. The hero now
   opens on the same lockup at building-sign scale: mark, trade tier, place
   tier. The h1 IS the trade tier, so the keywords and the ceremony are the
   same element rather than competing for the top of the page. ------------- */
/* THE RATIO. On Frank's wall the script is enormous and the trade tier is
   about a fifth of its height — that gap is what makes it read as signage
   rather than as a headline with a logo above it. This was running at
   roughly 1:0.55, so the two fused into one cream mass. */
/* With the wordmark gone the trade tier IS the hero, so it scales up from a
   1.6rem label to a 4rem headline. The place tier moved out of the h1 and up
   into the eyebrow, which is how every other band on the page opens — one
   fewer bespoke arrangement. */
/* .headline, not h1 — see the note on .intro. The class carries the look; the
   tag carries the meaning, and which tag it is depends on the page.
   NOT .h1: Bootstrap owns that name, and this project has already been bitten
   twice by borrowing one of theirs. */
.hero .headline{
  margin:0;
  font-size:clamp(1.85rem,3.9vw,3.15rem);
  line-height:1.06;letter-spacing:.005em;text-wrap:initial;
}
.hero .note{margin-top:1.25rem;max-width:44ch}
/* the checker under the hero was reading as a thin stripe — give it body */

/* ==========================================================================
   19. THE HERO — no panel
   The sign panel is gone. It gave the type a guaranteed ground, but it also
   dropped a heavy dark rectangle on a high-key photograph and became the
   loudest object in the frame.

   What replaces it is a CREAM VEIL rather than a dark scrim: the left of the
   image washes toward the brand cream, the type flips to ink, and the wordmark
   goes red. The photograph keeps its brightness — a dark wash would have
   muddied exactly what makes this frame good — and there is no edge anywhere.
   ========================================================================== */
/* ==========================================================================
   20. CARRIED BACK FROM THE LIVE SITE
   Four devices the production site already owns that this prototype had
   dropped. Evaluated before adopting — the lime scallop strip
   (_circles-green.twig) was deliberately left behind: the guide names the
   checkerboard as THE texture, a second full pattern dilutes it, and it is
   drawn in the lime that was demoted to keep red away from green.
   ========================================================================== */

/* --- 20a. The Mini Royale sub-brand. The live site has a finished badge for
   it and this prototype had mini golf as one of four identical cards. It is
   a separate revenue line, a separate audience and very likely a higher
   search volume than the par 3 — it earns its own band and its own mark. -- */
.stickered{position:relative;margin:0}
/* :not(.sticker) — .stickered img (0,1,1) otherwise out-specifies .sticker
   (0,1,0) and stretches the badge to the full frame. Same trap as .nav a
   versus .btn: scope a container's element selector away from components
   that can sit inside it. */
.stickered img:not(.sticker){width:100%;aspect-ratio:4/3;object-fit:cover;display:block}
.stickered .sticker{
  position:absolute;z-index:3;
  width:clamp(104px,15vw,168px);height:auto;
  /* top-left, hung off both edges so it reads as applied to the photograph
     rather than placed inside it */
  left:-5%;top:-7%;bottom:auto;
  /* a resting tilt, because a sticker pressed on by hand is never square */
  transform:rotate(-7deg);
  transform-origin:50% 50%;
  transition:transform .5s var(--ease);
  filter:drop-shadow(0 10px 20px rgba(36,29,18,.32));
}
/* the twist happens on the FIGURE, not the sticker — the whole photograph is
   the target, so the sticker reacts to the image being hovered rather than
   needing to be hit itself */
.stickered:hover .sticker{transform:rotate(3deg) scale(1.05)}
@media (prefers-reduced-motion:reduce){
  .stickered .sticker,.stickered:hover .sticker{transition:none}
}

/* --- 20b. The checkerboard breaking the image edge. The live site overlaps
   it onto photography rather than only fencing bands with it. Used ONCE,
   here, because this is precisely the device that tipped earlier passes into
   clutter. Three rows, which is the red variant's row count on production
   (_checkers-red-3row.twig) — the live system treats row count as a
   deliberate variable and this prototype had only one scale. --------------- */
/* This one redeclared the whole pattern at 9px cells while every other strip
   ran at 36px — two definitions of the same device drifting apart. It now
   carries .checker in the markup and overrides POSITION only, so there is one
   pattern in the stylesheet and the scale matches throughout. */
.checker-break{
  z-index:2;
  right:-6%;
  bottom:calc(var(--chk-cell) * -1);
  width:min(74%,468px);
  width:round(down, min(74%,468px), var(--chk-cell));
}
@media (max-width:900px){
  .checker-break{right:0;width:min(80%,396px);width:round(down,min(80%,396px),var(--chk-cell))}
}
@media (max-width:640px){
  /* -5% of a full-width figure is wider than the gutter on a phone, so the
     sticker escaped the viewport. Pulled in to stay inside it. */
  .stickered .sticker{width:88px;left:-3%;top:-5%;bottom:auto}
}

/* --- 20c. Video. The prototype had no motion at all, and night golf — the
   one thing nobody else nearby offers — is far more persuasive moving than
   still. Facade pattern: poster plus a play control, and the Vimeo iframe is
   only injected on click, so nothing third-party loads until asked. ------- */
.video{
  position:relative;display:block;width:100%;padding:0;border:0;cursor:pointer;
  background:var(--char);overflow:hidden;aspect-ratio:16/9;
}
.video img{width:100%;height:100%;object-fit:cover;transition:transform .6s var(--ease),filter .4s}
.video:hover img{transform:scale(1.03);filter:brightness(1.06)}
.video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
/* The play button. A flat red disc with a triangle was doing the job and
   nothing else. It now reads as part of the brand: a cream ring around a red
   disc, the same construction as the seal, with the ring opening outward on
   hover. */
.video__play{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none}
.video__play i{
  position:relative;
  width:clamp(64px,7.5vw,92px);aspect-ratio:1;border-radius:50%;
  background:var(--royale);display:grid;place-items:center;
  box-shadow:0 0 0 3px var(--cream), 0 10px 30px rgba(36,29,18,.45);
  transition:transform .35s var(--ease),box-shadow .35s var(--ease);
}
.video__play i::after{
  content:"";position:absolute;inset:-14px;border-radius:50%;
  border:1px solid rgba(242,237,215,.55);
  transform:scale(.88);opacity:0;
  transition:transform .45s var(--ease),opacity .45s var(--ease);
}
.video:hover .video__play i{
  transform:scale(1.06);
  box-shadow:0 0 0 3px var(--cream), 0 14px 38px rgba(36,29,18,.5);
}
.video:hover .video__play i::after{transform:scale(1);opacity:1}
.video__play svg{width:30%;height:30%;fill:var(--cream);margin-left:9%}

/* Plyr, themed. Only the tokens it exposes — no overriding its internals. */
.video .plyr{--plyr-color-main:var(--royale);--plyr-video-background:var(--char);
  --plyr-badge-background:var(--royale);--plyr-range-thumb-background:var(--cream);
  position:absolute;inset:0;height:100%}
.video .plyr__video-embed,.video .plyr iframe{position:absolute;inset:0;width:100%;height:100%;border:0}

/* --- 20d. The script as atmosphere. The live newsletter band sets the script
   enormous and low-contrast — tonal, not a statement. A third legitimate use
   beyond the wordmark and Roy's line, and it warms the footer for nothing. */
/* scoped to .footer, not .footer__brand — it moved into the signup row and
   silently lost the script face when the old selector stopped matching */
.footer .foot-script{
  font-family:var(--script);font-weight:400;
  font-size:clamp(2.9rem,5.4vw,4.6rem);line-height:.92;
  color:#5b5344;
  max-width:none;margin:0 0 .1rem;
  pointer-events:none;user-select:none;
}

/* --- 20e. The pine branch. A photographic cutout of the namesake, already on
   production, used there oversized and rotated off a corner so the frame
   crops it — a botanical specimen breaking the edge, which is exactly how
   old seed catalogues and enamel signage handled a natural motif.

   It adds the one register this page had none of: something organic and
   irregular among all the flat colour, type and rectangles.

   ONE HARD RULE: never on red. Pine green on Royale Red is a Christmas
   wreath, and that is the trap this whole palette has been steering around.
   Char and cream only. ---------------------------------------------------- */
.branch{
  position:absolute;z-index:0;pointer-events:none;user-select:none;
  width:min(52%,560px);height:auto;opacity:.5;
}
.branch--tl{top:0;left:0;transform:translate(-32%,-34%) rotate(90deg)}
.branch--br{bottom:0;right:0;transform:translate(30%,32%) rotate(-90deg)}
.band .wrap{position:relative;z-index:1}
@media (max-width:720px){.branch{width:min(74%,360px);opacity:.36}}

/* At phone width the hero box is taller than the 3:2 frame, so cover crops the
   SIDES and lands on the blurred middle — throwing away the two faces that are
   the whole reason this frame was chosen. Pull the crop left onto the nearer
   face. Landscape crops keep the centred composition. */
@media (max-width:640px){
  /* subjects are hard right in this frame — follow them, not the empty grass */
  .hero__bg img{object-position:78% 46%}
}


.hero .claims{margin-top:clamp(1.5rem,3vw,2.2rem)}
.hero .claims div{padding-right:1.4rem}
@media (min-width:640px){
  .hero .claims div{padding-left:1.4rem}
  .hero .claims div:first-child{padding-left:0}
}

/* The veil is a HORIZONTAL wash, which only works while the type occupies the
   left of a wide frame. Once the lockup stacks and fills the width, the right
   of every line falls off the fade — so below the lockup's breakpoint the
   veil turns vertical instead, strong where the type sits and releasing at
   the foot so the grass still reads. */
@media (max-width:759px){.hero{display:block;min-height:0;background:var(--cream)}.hero__bg{position:relative;height:clamp(245px,54vh,520px);height:clamp(245px,54svh,520px)}.hero__bg::after{background:linear-gradient(180deg,rgba(242,237,215,0) 46%,rgba(242,237,215,.5) 72%,rgba(242,237,215,.9) 88%,rgba(242,237,215,1) 100%)}.hero .wrap{position:relative;margin-top:clamp(-7rem,-17vw,-4.5rem);padding-block:0 clamp(2.5rem,8vw,4rem)}.hero__copy{max-width:none}.hero .note{text-align:left;width:fit-content;max-width:min(44ch,100%);margin-inline:auto}}


/* Once the lockup stacks there is no left column to align to, so the whole
   block centres rather than hanging off an edge that no longer exists. */
@media (max-width:759px){.hero__copy{text-align:center}.hero__actions{justify-content:center}.hero .claims div{text-align:center;padding-inline:0}.hero .headline{text-align:center}.hero .note{margin-inline:auto}}


/* --- The CTA row. Two buttons and a phone link: on a local business page the
   call is the highest-intent action there is, and it had been living only in
   the 13px utility bar. It sits after the buttons, quiet, not competing. */
.hero__actions{align-items:center;gap:.7rem 1rem}
.hero__tel{
  font-family:var(--display);font-weight:500;font-size:.92rem;
  color:var(--fg-soft);border-bottom:1px solid rgba(36,29,18,.3);
  padding-bottom:.1rem;
}
.hero__tel:hover{color:var(--char);border-bottom-color:var(--char)}
@media (max-width:759px){
  .hero__tel{width:100%;text-align:center;border-bottom:0;margin-top:.2rem}
}


/* --- The poster face in the navigation, top and bottom.
   Alfa Slab at 13px caps would be a brick, so the nav takes it a touch
   smaller with the tracking pulled in from .16em to .07em — a slab needs far
   less letter-spacing than a geometric sans to breathe. The footer column
   heads take it too, so the device reads as deliberate at both ends of the
   page rather than as a one-off in the header. */
.nav > li > a:not(.btn){
  font-family:var(--poster);font-weight:400;
  font-size:.78rem;letter-spacing:.07em;
}
.footer__nav h4{font-family:var(--poster);font-weight:400;letter-spacing:.09em;font-size:.76rem}

/* --- Cards on the red field.
   They had been red-on-red with a hairline, so they were invisible boxes. As
   light tiles they read as a menu board, and the copy inside gets a proper
   ground: char on cream is 10.4:1 against 4.9:1 on the red. The 1px gap now
   shows the red through, so the field still holds the grid together. */
.theme-red .games{background:var(--royale-deep);border-color:transparent}
.theme-red .game{background:var(--shell)}
.theme-red .game:hover{background:#f2ecd8}
.theme-red .game > span{color:var(--royale-ink)}
.theme-red .game h3,.theme-red .game p{color:var(--char)}
.theme-red .game p{color:#5f5632}
.theme-red .game .text-link{color:var(--royale-ink)}

/* ==========================================================================
   21. SUBNAV
   Two groups only — Golf and What's Happening — not the four mega panels an
   early pass had. A small compact list is the right weight for a property
   this size; a full-width panel was scaffolding for a building that isn't
   there. Rates moved under Golf, where it belongs, rather than sitting at
   top level competing with the revenue lines.
   ========================================================================== */
.nav > li{position:relative}
.nav .navlink{
  font-family:var(--poster);font-weight:400;
  font-size:.78rem;letter-spacing:.07em;text-transform:uppercase;
  background:none;border:0;cursor:pointer;color:var(--pine);
  padding:.6rem .7rem;display:inline-flex;align-items:center;gap:.45rem;
  font-family:var(--poster);
}
.nav .navlink:hover{color:var(--royale-ink)}
.nav .navlink .crt{width:9px;height:6px;transition:transform .3s var(--ease)}
.has-sub:hover .navlink .crt,
.has-sub:focus-within .navlink .crt{transform:rotate(180deg)}
.has-sub:hover .navlink,
.has-sub:focus-within .navlink{color:var(--royale-ink)}

.subnav{
  position:absolute;top:100%;left:.2rem;z-index:70;
  min-width:14rem;margin:0;padding:0;list-style:none;
  background:var(--shell);
  border:1px solid rgba(36,29,18,.16);
  box-shadow:0 14px 34px rgba(36,29,18,.16);
  opacity:0;visibility:hidden;transform:translateY(-6px);
  transition:opacity .22s var(--ease),transform .22s var(--ease),visibility .22s;
}
.has-sub:hover .subnav,
.has-sub:focus-within .subnav{opacity:1;visibility:visible;transform:none}
.subnav li + li{border-top:1px solid rgba(36,29,18,.1)}
.subnav a{
  display:block;padding:.6rem 1.15rem;
  /* the display face here, not the slab: the slab is the top level's voice,
     and a second tier set in it would flatten the hierarchy */
  font-family:var(--display);font-weight:500;font-size:.92rem;letter-spacing:.01em;
  color:var(--pine);
}
/* a tint, not an underline — the sliding rule is the top level's gesture and
   repeating it one tier down says the two are the same thing */
.subnav a:hover{background:rgba(196,48,29,.08);color:var(--royale-ink)}
.subnav a::after{display:none}



/* ==========================================================================
   22. MOBILE NAV
   Rebuilt. The previous version put a border-bottom on every li, so a group's
   separator landed BELOW its children — the rule appeared after "Membership"
   rather than after "Golf", which made the grouping unreadable. And the
   sub-list carried a border-left, which is the stray vertical line.

   Now: a rule ABOVE each top-level group, sub-items indented with no rule at
   all, and the two tiers set in the two faces the desktop nav already uses.
   ========================================================================== */
/* It now lives inside the sticky header, so it opens under the bar at any
   scroll position. As a sibling after </header> it stayed where it sat in the
   document — scroll down and the burger opened a panel far up the page, which
   read as "the nav only works at the top". */
/* A full-screen overlay, not a panel in the flow. As a block after </header>
   it opened wherever it happened to sit in the document, so scrolling down
   and tapping the burger revealed nothing — position:fixed makes that
   structurally impossible. */
.mobile-nav{
  position:fixed;inset:0;z-index:100;
  background:var(--cream);
  overflow-y:auto;-webkit-overflow-scrolling:touch;
  opacity:0;visibility:hidden;
  transform:translateY(-1.5rem);
  transition:opacity .4s var(--ease),transform .4s var(--ease),visibility .4s;
}
.mobile-nav[data-open="true"]{opacity:1;visibility:visible;transform:none}

/* the overlay carries its own bar, because it covers the real one */
.mobile-nav__bar{
  display:flex;align-items:center;justify-content:space-between;gap:1.5rem;
  padding:1rem var(--gutter);
  border-bottom:1px solid var(--hair);
}
.mobile-nav__bar .logo img{height:clamp(46px,11vw,60px);width:auto}
.burger--close{border:0;padding:.5rem;color:var(--pine);display:grid;place-items:center}
.burger--close svg{width:22px;height:22px;display:block}
.burger--close:hover{color:var(--royale-ink)}

/* items settle in one after another — 40ms apart, enough to read as a
   sequence without anybody waiting for it */
.mobile-nav > .wrap > ul > li,
.mobile-nav > .wrap > .btn{
  opacity:0;transform:translateY(12px);
  transition:opacity .45s var(--ease),transform .45s var(--ease);
}
.mobile-nav[data-open="true"] > .wrap > ul > li,
.mobile-nav[data-open="true"] > .wrap > .btn{opacity:1;transform:none}
.mobile-nav[data-open="true"] > .wrap > ul > li:nth-child(1){transition-delay:.10s}
.mobile-nav[data-open="true"] > .wrap > ul > li:nth-child(2){transition-delay:.14s}
.mobile-nav[data-open="true"] > .wrap > ul > li:nth-child(3){transition-delay:.18s}
.mobile-nav[data-open="true"] > .wrap > ul > li:nth-child(4){transition-delay:.22s}
.mobile-nav[data-open="true"] > .wrap > ul > li:nth-child(5){transition-delay:.26s}
.mobile-nav[data-open="true"] > .wrap > .btn{transition-delay:.30s}

/* scroll lock — both elements, because which one scrolls varies by engine */
html.nav-open,html.nav-open body{overflow:hidden}

@media (prefers-reduced-motion:reduce){
  .mobile-nav{transition:opacity .001ms,visibility .001ms;transform:none}
  .mobile-nav > .wrap > ul > li,.mobile-nav > .wrap > .btn{transition:none;opacity:1;transform:none}
}
.mobile-nav .wrap{padding-block:1rem 2.5rem}
.mobile-nav ul{list-style:none;margin:0;padding:0}

.mobile-nav > .wrap > ul > li{border-top:1px solid var(--hair)}
.mobile-nav > .wrap > ul > li:first-child{border-top:0}

.mobile-nav > .wrap > ul > li > a{
  display:block;padding:.95rem 0 .55rem;
  font-family:var(--poster);font-weight:400;
  font-size:1.12rem;letter-spacing:.03em;text-transform:uppercase;
  color:var(--pine);
}
.mobile-nav > .wrap > ul > li > a:only-child{padding-block:.95rem}
.mobile-nav > .wrap > ul > li > a:hover{color:var(--royale-ink)}

.mobile-sub{padding:0 0 .85rem .1rem!important;margin:0!important}
.mobile-sub a{
  display:block;padding:.34rem 0;
  font-family:var(--display);font-weight:400;font-size:1rem;letter-spacing:.005em;
  color:var(--fg-soft);
}
.mobile-sub a::before{
  content:"";display:inline-block;width:.85rem;height:1px;
  background:var(--royale);vertical-align:.3em;margin-right:.6rem;
}
.mobile-sub a:hover{color:var(--royale-ink)}

.mobile-nav .btn{margin-top:1.4rem;width:100%;justify-content:center}

/* ==========================================================================
   23. THE LOCATOR
   A schematic, not a map. Drawing real street geometry would mean inventing
   it, and a vintage roadside "how to find us" diagram is more on-brand than
   a grey Google tile anyway. Nothing here asserts a road name or a distance
   that isn't already published.
   ========================================================================== */
.locator{margin:0}
/* the same double rule as the scorecard and the old plaque — outer stroke,
   a gap, inner hairline. A single flat border was the one thing in the Visit
   band not speaking the same language as the rest of the page. */
.locator__frame{
  position:relative;
  /* a warm mid-tone, not --char: a solid near-black frame around something
     this large stopped reading as a rule and started reading as a box */
  border:1.5px solid rgba(45,39,20,.38);
  padding:7px;background:var(--shell);
}
.locator__frame > svg{width:100%;height:auto;display:block;border:1px solid rgba(45,39,20,.16)}
.lc-ground{fill:var(--shell)}
.lc-wood{fill:rgba(45,39,20,.07)}
.lc-road{stroke:var(--sand);stroke-width:9;fill:none;stroke-linecap:round}
.lc-road--major{stroke-width:15}
.lc-road--minor{stroke-width:6}
.lc-route{
  stroke:var(--royale);stroke-width:2.5;fill:none;
  stroke-dasharray:7 7;stroke-linecap:round;
}
.lc-dot{fill:var(--char)}
.lc-pin circle{fill:var(--royale)}
.lc-pin__mark{
  font-family:var(--display);font-weight:500;font-size:12px;
  fill:var(--cream);letter-spacing:.06em;
}
.lc-label{
  font-family:var(--display);font-weight:500;font-size:10.5px;
  letter-spacing:.18em;fill:var(--char);text-transform:uppercase;
}
.lc-label--pin{fill:var(--royale-ink)}
.lc-label--soft{fill:var(--fg-soft);letter-spacing:.14em}
.lc-compass path{fill:var(--char)}
.lc-compass__ring{fill:none;stroke:rgba(45,39,20,.3);stroke-width:1}
.locator figcaption{
  margin-top:.75rem;font-family:var(--display);font-weight:400;
  font-size:.78rem;letter-spacing:.04em;color:var(--fg-soft);
}

/* the live map sits over the schematic, which stays as the fallback — if the
   key, the network or the API fails, the diagram is still there */
.locator{position:relative}
.locator__live{
  position:absolute;inset:7px;z-index:2;
  border:1px solid rgba(45,39,20,.16);
  opacity:0;transition:opacity .6s var(--ease);
  pointer-events:none;
}
.locator__live.is-ready{opacity:1;pointer-events:auto}

.has-js .locator > .locator__frame > svg{opacity:0}.has-js .locator.map-failed > .locator__frame > svg{opacity:1;transition:opacity .35s var(--ease)}
.locator__live .gm-style-cc,.locator__live .gmnoprint{filter:saturate(.5)}


/* --- The signup row.
   It used to sit at the foot of the brand column, stacked under the blurb —
   which buried it. Now it is its own full-width row with a rule above,
   split two columns: the line on the left, the form on the right. */
.footer__signup{
  position:relative;                       /* the arrow sits behind this band */
  border-top:1px solid rgba(242,237,215,.18);
  --bs-gutter-x:clamp(1.5rem,3vw,3rem);--bs-gutter-y:1.25rem;
  padding-block:clamp(1.75rem,3.5vw,2.75rem);
}
/* the script has deep descenders — the label was sitting inside the tail of
   the "y" and the "g" */
.footer__signup-lede .foot-script{margin:0 0 .9rem}
/* shown only to CP users, when no signup form has been picked */
.footer__signup-empty{
  margin:0;font-size:.9rem;line-height:1.5;color:rgba(242,237,215,.62);
  border:1px dashed rgba(242,237,215,.3);padding:.75rem .9rem;
}
.footer__signup-empty a{text-decoration:underline;text-underline-offset:2px}
.footer__signup-lede label{
  display:block;font-family:var(--display);font-weight:500;
  font-size:1.02rem;color:var(--cream);
}
.newsletter{margin:0}


/* Roy lifts into the checkerboard above him, and grows on small screens where
   he had been shrinking to a thumbnail. */
@media (min-width:1000px){
  .roy{margin-top:clamp(-9.5rem,-7.5vw,-5rem)}
}
@media (max-width:999px){
  .roy{margin-top:-3.5rem}
  .roy svg{max-width:min(70vw,340px)}
  .roy figcaption{font-size:1.7rem}
}
/* between 641 and 900 the split has already stacked, so the figure is
   full-width and -5% is again wider than the gutter */
@media (max-width:900px){
  .stickered .sticker{left:-3%}
}

/* a hand-drawn arrow between the line and the form — the one flourish in the
   footer, and the kind of thing a painted sign would actually have */
.signup-arrow{display:none}
@media (min-width:780px){
  .signup-arrow{
    /* Not a column between the script and the form — a MARK ACROSS THE BAND,
       behind both. As a flex item it could only ever win space from its
       neighbours, and it did: the lede wrapped and the field stacked under its
       button. Out of the flow it runs the full width, which is what a dart this
       long wants, and the type sits on top of it the way it would on a painted
       sign. */
    display:block;
    /* Out of the flex flow, but sized and placed to occupy the GAP between the
       script and the form rather than the whole band — it is pointing from one
       to the other, so that run of empty space is the thing it belongs in.
       Being absolute means it can be as big as the gap allows without taking
       width off either neighbour. z-index 0 keeps it under the script's
       descenders if they reach. */
    position:absolute;
    /* 32% -> 54%, so it tucks BEHIND both neighbours rather than squeezing
       into the gap between them: the tail runs under the end of "touch", the
       point well under the field. That only works now it is tonal —
       at red it would have read as a mistake. z-index 0 keeps it under both. */
    left:30.5%;width:36%;
    top:46%;transform:translateY(-50%) rotate(-1.5deg);
    height:auto;
    z-index:0;pointer-events:none;
  }
  /* everything else in the band rides above it */
  .footer__signup > *:not(.signup-arrow){position:relative;z-index:1}
}

/* --- The retro arrow.
   A long mid-century "this way" dart with five speed lines stacked behind it,
   the mark you'd find painted on a roadside sign. Supplied as artwork, recoloured
   off the brand: the bars in sand, the dart in Royale Red, so it reads as one
   object catching the light rather than six shapes of the same weight.

   The bars run in sequence LEFT TO RIGHT, into the dart, so it reads as travel
   in the direction the arrow points — and the dart answers at the end of the
   cycle rather than pulsing along with them. */
/* TONAL, not red. Any red here competes with the Sign Up button a few inches
   away, and there should only be one loud thing in this band. These are two
   steps up from --char (#241d12) — enough to read as a painted mark on a dark
   wall, not enough to ask for attention. Same move as the footer script, which
   is cream pre-blended over char at #474033 rather than a translucent fill. */
.signup-arrow .ra-bar{fill:#4b4132}
.signup-arrow .ra-head{fill:#3b3123}

@keyframes ra-run{
  0%,55%,100%{opacity:.5;transform:translateX(-1.5px)}
  22%        {opacity:1; transform:translateX(1.5px)}
}
@keyframes ra-lead{
  0%,60%,100%{opacity:.9;transform:translateX(0)}
  80%        {opacity:1; transform:translateX(4px)}
}
.signup-arrow .ra-bar,
.signup-arrow .ra-head{
  animation:ra-run 2.4s var(--ease) infinite;
  transform-box:fill-box;transform-origin:center;
}
.signup-arrow .ra-bar:nth-of-type(2){animation-delay:.09s}
.signup-arrow .ra-bar:nth-of-type(3){animation-delay:.18s}
.signup-arrow .ra-bar:nth-of-type(4){animation-delay:.27s}
.signup-arrow .ra-bar:nth-of-type(5){animation-delay:.36s}
.signup-arrow .ra-head{animation-name:ra-lead;animation-delay:.45s}

/* the whole mark nudges when the form is being used */
.footer__signup:focus-within .signup-arrow{
  transform:translateY(-50%) rotate(-1.5deg) translateX(10px);
}
.signup-arrow{transition:transform .45s var(--ease),opacity .45s var(--ease)}

@media (prefers-reduced-motion:reduce){
  .signup-arrow .ra-bar,.signup-arrow .ra-head{animation:none}
  .signup-arrow .ra-bar{opacity:.75}
}

/* the caption row under the map: what it is on the left, what to do on the
   right — the action sits with the thing it acts on rather than orphaned up
   beside the address */
.locator figcaption.locator__cap{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:.6rem 1.5rem;margin-top:.9rem;
}
.locator__cap > span{
  font-family:var(--display);font-weight:400;font-size:.86rem;
  letter-spacing:.03em;color:var(--fg-soft);
}
.locator__cap .text-link{align-self:center;padding-bottom:.18rem}

/* --- The map card.
   Google's InfoWindow chrome can only be nudged, not replaced — the white
   bubble and its tail are the API's. What IS ours is the content, so the card
   inside is set in the page's own faces and colours and the bubble is pulled
   back to a plain white sheet around it. */
.gm-style .gm-style-iw-c{
  border-radius:2px;padding:0;
  box-shadow:0 10px 30px rgba(36,29,18,.28);
  max-height:none!important;
}
/* Google sets an inline max-height on the inner scroller, which was cutting
   the two buttons off the bottom of the card. */
.gm-style .gm-style-iw-d{overflow:visible!important;max-height:none!important;padding:0}
/* and the bubble takes browser focus when it opens — that blue ring is the UA
   default, not ours */
.gm-style .gm-style-iw-c:focus,
.gm-style .gm-style-iw-c:focus-visible,
.gm-style .gm-style-iw:focus{outline:none}
.gm-style .gm-style-iw-chr{height:0;overflow:visible}
.gm-style .gm-style-iw-chr button{top:2px!important;right:2px!important}
.gm-style .gm-style-iw-tc::after{background:#fff}
.mapcard{padding:1.1rem 1.25rem .95rem;min-width:14rem;font-family:var(--body)}
.mapcard__name{
  font-family:var(--poster);text-transform:uppercase;
  font-size:1.15rem;line-height:1;letter-spacing:.01em;
  color:var(--royale-deep);margin:0 0 .5rem;
}
.mapcard__addr{font-size:.95rem;line-height:1.45;color:var(--pine);margin:0 0 .7rem;font-weight:400}
.mapcard__hours{
  font-size:.82rem;line-height:1.55;color:#5f5632;margin:0 0 .85rem;font-weight:400;
  border-top:1px solid rgba(45,39,20,.16);padding-top:.6rem;
}
.mapcard__hours b{font-family:var(--display);font-weight:500;color:var(--pine)}
.mapcard__acts{display:flex;gap:.55rem;margin:0;flex-wrap:wrap}
.mapcard__acts a{
  font-family:var(--display);font-weight:500;font-size:.78rem;
  letter-spacing:.09em;text-transform:uppercase;
  padding:.45rem .8rem;border-radius:100px;
  background:var(--royale);color:var(--cream);
}
.mapcard__acts a + a{background:transparent;color:var(--royale-deep);border:1px solid rgba(196,48,29,.45)}


/* ==========================================================================
   24. STRUCTURE CLASSES
   These replace nineteen inline style attributes that had accumulated in the
   markup — max-widths, margin-tops and two hardcoded colours. Nothing in the
   HTML now carries a style attribute, so every measurement lives in one file
   and a CMS can output the markup without carrying presentation with it.
   ========================================================================== */

/* the intro block that opens a band */
.band-head{max-width:42rem}
.band-head--center{margin-inline:auto;text-align:center}
/* A flex row with a real gap, not two inline-blocks relying on the whitespace
   between them — inside .band--center the container was already display:flex,
   which collapses that whitespace, so the buttons ended up touching. Same gap
   as .hero__actions so every CTA pair on the site sits the same way. */
.band-actions{
  margin-top:2rem;
  display:flex;flex-wrap:wrap;align-items:center;
  gap:.7rem 1rem;
}

/* .note always answers a heading, so its spacing is structural — reached by
   nesting rather than set on each instance */
h1 + .note,h2 + .note,h3 + .note,.headline + .note{margin-top:1.3rem}
.note--center{margin-inline:auto;text-align:center}

/* band modifiers that had been inline */
.band--sign{overflow:hidden}
.band--center .wrap{text-align:center}
.band--center .hero__actions,.band--center .band-actions{display:flex;justify-content:center}

/* the two hardcoded rgba colours were doing what the theme tokens already do */
.member .note p{color:rgba(242,237,215,.72)}
.theme-red .note p{color:var(--fg-soft)}

figure{margin:0}

/* ==========================================================================
   25. INNER PAGES
   The subpage hero is shorter than the homepage's and carries no claim row —
   the home page has to introduce the whole property, an inner page has
   already been chosen.
   ========================================================================== */
/* Width-aware, not just height-aware. 46vh alone cannot see how wide the
   viewport is, so past about 1600px the frame held its height while the width
   ran away and the photograph flattened into a letterbox — 3.04:1 at 1440,
   4.73:1 at 2400. max(46vh, 33vw) holds roughly 3:1 as the screen widens and
   changes nothing at or below 1440, where 46vh still wins. */
.hero--inner{min-height:clamp(380px,max(46vh,33vw),820px)}
.hero--inner .wrap{padding-block:clamp(1.5rem,3vw,2.75rem) clamp(4.5rem,8vw,7rem)}
.hero--inner .headline{font-size:clamp(1.9rem,4vw,3.3rem)}
.hero--inner .hero__copy{max-width:36rem}
/* anchor near the top of the frame — these are pictures of people and a
   centred crop was cutting through their heads */
.hero--inner .hero__bg img{object-position:80% 18%}

@media (max-width:759px){
  /* The inner hero stacks like the homepage one. This block held the
     overlay version — its own veil, a min-height and 14rem of top
     padding to clear people's faces — all of which came later at the
     same specificity and beat the stacked rules, leaving these pages
     with a tall photograph and the text shoved below it. */
  .hero--inner .hero__copy{max-width:none}
}

/* the turf cutaway is a tall portrait diagram, not a 4:3 photograph */
.turf-diagram{width:100%;height:auto;aspect-ratio:auto!important;object-fit:contain!important}

/* the scorecard artwork sits under the data table as the real-world object */
.scorecard-art{margin-top:clamp(1.8rem,3vw,2.6rem)}
.scorecard-art img{width:100%;height:auto;border:2px solid rgba(45,39,20,.38)}
.scorecard-art figcaption{
  margin-top:.8rem;max-width:56ch;
  font-family:var(--display);font-weight:400;font-size:.84rem;line-height:1.5;
  color:var(--fg-soft);
}
/* nine holes plus a total is a lot of columns — let the row labels hold */
.scorecard--yards{min-width:46rem}
.scorecard--yards th[scope="row"]{min-width:5.5rem}
.scorecard--yards td{font-variant-numeric:tabular-nums}
/* the totals column is the payoff of the table, so it carries the accent */
.scorecard--yards tbody tr > *:last-child{color:var(--accent);font-weight:500}
.scorecard--yards tbody tr:last-child > *{color:var(--fg-soft);font-weight:400}

/* ==========================================================================
   26. MENU
   A menu is the one page where the content really is a list of name / price /
   description, so it earns a component. Nothing inside carries a class —
   the group wrapper reaches h3, h4, b and p by nesting, which maps 1:1 to a
   matrix row of three fields.
   ========================================================================== */
/* menu columns run long and uneven, so they top-align rather than centre
   the way the split bands do */
.menus{margin-top:clamp(2.4rem,4.5vw,3.6rem)}
.band .menus > [class*="col-"]{justify-content:flex-start}
.menu-group + .menu-group{margin-top:clamp(2.4rem,4vw,3.4rem)}
.menu-group h3{
  font-family:var(--display);font-weight:500;
  font-size:.78rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--accent);
  padding-bottom:.7rem;border-bottom:2px solid currentColor;
}
.menu-list{list-style:none;margin:0;padding:0}
/* the homepage teaser is the same list with no descriptions and a cap */
.menu-list--tight{margin:1.9rem 0 2rem;max-width:28rem}
.menu-list--tight li{padding:.62rem 0}
.menu-list li{padding:.95rem 0;border-bottom:1px solid var(--hair)}
.menu-list h4,.dishes h4{
  display:flex;align-items:baseline;
  font-family:var(--body);font-weight:700;font-size:1.02rem;line-height:1.3;
  color:var(--fg);
}
/* the dotted leader is drawn, not typed, so the name stays plain text */
.menu-list h4::after,.dishes h4::after{
  content:"";flex:1 1 auto;min-width:1.5rem;margin:0 .7rem;
  border-bottom:1px dotted var(--hair);transform:translateY(-.22em);
}
.menu-list b,.dishes b{
  flex:none;font-family:var(--display);font-weight:500;font-size:.95rem;
  color:var(--fg);font-variant-numeric:tabular-nums;white-space:nowrap;
}
.menu-list p{
  margin-top:.34rem;max-width:44ch;
  font-weight:400;font-size:.94rem;line-height:1.55;color:var(--fg-soft);
}
/* name + price share row one, the description wraps under both */
.menu-list li{
  display:grid;grid-template-columns:1fr auto;
  align-items:baseline;column-gap:.4rem;
}
/* no explicit rows: auto-placement in source order puts an optional photo on
   its own row above the name, and an item without one starts at row 1 */
.menu-list h4{grid-column:1}
.menu-list b{grid-column:2}
.menu-list p{grid-column:1 / -1}
/* a phone shows one long column instead of two, so the rhythm tightens to
   keep a section readable without endless scrolling */
@media (max-width:719px){
  .menu-list li{padding:.8rem 0}
  .menu-list h4{font-size:.98rem}
  .menu-list p{margin-top:.28rem;font-size:.88rem;line-height:1.5}
  .menu-group + .menu-group{margin-top:2.2rem}
}

/* --------------------------------------------------------------------------
   27. A FEW SELECTS
   Not a gallery — a handful of items in the menu carry a photograph, the way a
   printed menu photographs the two or three things it wants you to order. In
   Craft this is an optional image field on the menu-item entry type, so an
   editor turns a photo on per row and the list absorbs it.
   -------------------------------------------------------------------------- */
.menu-list img{
  grid-column:1 / -1;
  width:100%;aspect-ratio:3/2;object-fit:cover;
  margin-bottom:.9rem;
}
/* an item with a photo gets air above it so the picture reads as a feature
   rather than as the previous item's footer */
.menu-list li:has(img){padding-top:1.6rem}

/* a figure inside a band row gets a caption in the same quiet voice as the
   scorecard's — it explains the photograph, it is not a heading */
.band .row figure figcaption{
  margin-top:.8rem;
  font-family:var(--display);font-weight:400;font-size:.84rem;line-height:1.5;
  color:var(--fg-soft);
}

/* --------------------------------------------------------------------------
   27. GALLERY
   Four small tiles and one large one, weighted to a corner rather than laid
   out evenly — the same asymmetry the checkerboard strips use. The tiles are
   square so nothing has to be re-cropped per page; the feature tile is the
   only one that changes shape.
   -------------------------------------------------------------------------- */
.gallery{
  list-style:none;margin:clamp(2.4rem,4.5vw,3.6rem) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(.6rem,1.2vw,1.1rem);
}
.gallery li{margin:0}
.gallery img{display:block;width:100%;height:100%;object-fit:cover;aspect-ratio:1}
/* the feature tile: two by two, sitting first so the block reads left-heavy */
.gallery li:first-child{grid-column:span 2;grid-row:span 2}
.gallery li:first-child img{aspect-ratio:auto}

@media (max-width:719px){
  .gallery{grid-template-columns:repeat(2,1fr)}
  .gallery li:first-child{grid-column:span 2;grid-row:auto}
  .gallery li:first-child img{aspect-ratio:3/2}
}

/* --------------------------------------------------------------------------
   28. LIGHTBOX
   The grid shows five; the rest of the set is in the same <ul>, hidden. One
   list is the source of truth, so the extra frames are still in the markup
   for a crawler and the button has nothing to fetch. In Craft this is one
   images field — the template prints them all and hides everything past the
   fifth.
   -------------------------------------------------------------------------- */
.gallery li[hidden]{display:none}
.gallery li{cursor:zoom-in}
.gallery + .band-actions{margin-top:clamp(1.6rem,2.6vw,2.2rem)}

.lightbox{
  position:fixed;inset:0;z-index:200;
  background:rgba(28,22,13,.97);
  display:grid;grid-template-rows:auto 1fr auto;
  opacity:0;visibility:hidden;
  transition:opacity .28s var(--ease),visibility .28s var(--ease);
}
.lightbox[data-open="true"]{opacity:1;visibility:visible}
.lightbox__bar{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:clamp(.9rem,2vw,1.4rem) var(--gutter);
  border-bottom:1px solid rgba(242,237,215,.16);
}
.lightbox__count{
  font-family:var(--display);font-weight:500;font-size:.76rem;
  letter-spacing:.2em;text-transform:uppercase;color:var(--sand);
  font-variant-numeric:tabular-nums;
}
.lightbox__close{
  width:2.4rem;height:2.4rem;flex:none;display:grid;place-items:center;
  background:none;border:1px solid rgba(242,237,215,.35);border-radius:50%;
  color:var(--cream);cursor:pointer;
  transition:border-color .3s var(--ease),background .3s var(--ease);
}
.lightbox__close:hover{border-color:var(--cream);background:rgba(242,237,215,.1)}
.lightbox__close svg{width:14px;height:14px;display:block}

.lightbox__stage{position:relative;overflow:hidden;min-height:0}
/* Fit, whatever the shape. A percentage height on the image never resolved
   reliably against a grid row, so the portrait frames filled the width and got
   clipped. Absolute insets plus margin:auto is the bulletproof version: the
   box is the stage, max-width/max-height resolve against it because it is the
   positioned ancestor, and the image scales down to fit and centres in both
   axes whichever way round it is. */
.lightbox__stage img{
  position:absolute;
  top:clamp(1rem,3vw,2rem);bottom:clamp(1rem,3vw,2rem);
  left:var(--gutter);right:var(--gutter);
  width:auto;height:auto;margin:auto;
  max-width:100%;max-height:100%;
  display:block;
}
.lightbox__nav{
  position:absolute;top:50%;transform:translateY(-50%);
  width:3rem;height:3rem;display:grid;place-items:center;
  background:rgba(28,22,13,.55);border:1px solid rgba(242,237,215,.3);border-radius:50%;
  color:var(--cream);cursor:pointer;
  transition:border-color .3s var(--ease),background .3s var(--ease);
}
.lightbox__nav:hover{border-color:var(--cream);background:rgba(28,22,13,.85)}
.lightbox__nav svg{width:15px;height:12px;display:block}
.lightbox__nav--prev{left:clamp(.5rem,2vw,1.5rem);transform:translateY(-50%) rotate(180deg)}
.lightbox__nav--next{right:clamp(.5rem,2vw,1.5rem)}

/* the strip doubles as the "more photos" the button promised */
.lightbox__strip{
  display:flex;gap:.5rem;overflow-x:auto;-webkit-overflow-scrolling:touch;
  padding:clamp(.8rem,1.8vw,1.1rem) var(--gutter);
  border-top:1px solid rgba(242,237,215,.16);
}
.lightbox__strip button{
  flex:none;width:clamp(58px,7vw,84px);aspect-ratio:1;padding:0;
  background:none;border:0;cursor:pointer;opacity:.45;
  transition:opacity .25s var(--ease),outline-color .25s var(--ease);
  outline:2px solid transparent;outline-offset:-2px;
}
.lightbox__strip button:hover{opacity:.8}
.lightbox__strip button[aria-current="true"]{opacity:1;outline-color:var(--royale)}
.lightbox__strip img{width:100%;height:100%;object-fit:cover;display:block}

html.lb-open,html.lb-open body{overflow:hidden}
@media (max-width:719px){
  .lightbox__nav{width:2.5rem;height:2.5rem}
}

/* --------------------------------------------------------------------------
   29. CARDS, PULL QUOTE, UPDATES, FORM
   Four components the inner pages needed. Nothing inside any of them carries
   a class — the wrapper reaches h3, p, time, label and input by nesting, so a
   matrix row of plain fields renders straight into it.
   -------------------------------------------------------------------------- */

/* A ROW OF CARDS, not a split band. `.band .row > [class*="col-"]` centres its
   columns vertically, which is right when a band is one image beside one block
   of copy and wrong the moment there are three cards of unequal height — the
   pictures stop lining up. Same fix as .menus. */
.cards{margin-top:clamp(2.4rem,4.5vw,3.6rem)}
.band .cards > [class*="col-"]{justify-content:flex-start}

/* a simple picture-over-copy card, used for the three ways to split a group */
.card img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block;margin-bottom:1.2rem}
.card h3{
  font-family:var(--display);font-weight:500;
  font-size:.78rem;letter-spacing:.2em;text-transform:uppercase;
  color:var(--accent);margin-bottom:.6rem;
}
.card p{font-weight:400;font-size:.98rem;line-height:1.6;color:var(--fg-soft);max-width:34ch}

/* the one testimonial on the site gets a band to itself rather than a widget */
.pull{margin:0;max-width:46rem}
.pull p{
  font-family:var(--poster);font-weight:400;
  font-size:clamp(1.3rem,2.6vw,2.1rem);line-height:1.34;color:var(--fg);
}
.pull footer{
  margin-top:1.4rem;
  font-family:var(--display);font-weight:500;font-size:.74rem;
  letter-spacing:.2em;text-transform:uppercase;color:var(--accent);
}

/* Updates. The listing cards are PHOTOGRAPHS, so they cover — the flyer for a
   post lives on its detail page, beside the copy at its own proportion, where
   its headline is legible. A wall of cropped flyers was the wrong front door. */
.updates{
  list-style:none;margin:clamp(2.4rem,4.5vw,3.6rem) 0 0;padding:0;
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(2rem,3.4vw,3rem) clamp(1.4rem,2.4vw,2rem);
}
.updates li{margin:0}
.updates img{
  width:100%;aspect-ratio:4/3;object-fit:cover;display:block;
  background:var(--sand);margin-bottom:1.1rem;
}
.updates time{
  display:block;
  font-family:var(--display);font-weight:500;font-size:.72rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--accent);
  margin-bottom:.5rem;
}
.updates h3{font-family:var(--body);font-weight:700;font-size:1.06rem;line-height:1.3;margin-bottom:.45rem}
.updates p{font-weight:400;font-size:.94rem;line-height:1.55;color:var(--fg-soft);max-width:36ch}
@media (max-width:899px){.updates{grid-template-columns:repeat(2,1fr)}}
@media (max-width:539px){.updates{grid-template-columns:1fr}}
.jobs-empty{margin-top:clamp(2.4rem,4.5vw,3.6rem);padding-top:clamp(1.6rem,2.6vw,2.2rem);border-top:1px solid var(--hair);max-width:54ch;font-weight:400;font-size:clamp(1.02rem,1.25vw,1.18rem);line-height:1.64;color:var(--fg-soft)}

/* the enquiry form: same field treatment as the newsletter, on a light ground.
   There was a margin-top:clamp(.5rem,2vw,0) here. A unitless 0 is not a length
   inside clamp(), so browsers threw the declaration away and it never applied
   — Dart Sass refuses to compile it at all. Removed rather than guessed at;
   the form has always sat where it sits without it. */
/* `p` is the hand-built form, `.field` is what Freeform's containers carry.
   Both are one field's worth of vertical rhythm. */
.enquiry p,.enquiry .field{margin:0 0 1.1rem}
/* auto-columns rather than a fixed 1fr 1fr: Freeform rows hold however many
   fields the editor put in them, so a row of one goes full width and a row of
   three splits three ways without the CSS having to know in advance.
   minmax(0,…) because an email address in an auto-minimum track overflows. */
.enquiry .field-row{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:0 1.1rem}
/* ERROR STATES.
   Two sets of class names on purpose. The form posts by AJAX, and Freeform's
   JS injects its own names (freeform-form-errors / freeform-errors /
   freeform-has-errors) rather than the ones the formatting template sets — so
   both are dressed here. The markup rule is about editors never writing
   classes; a plugin's fixed names wearing our CSS is the same outcome.

   The FIELD carries the error first: a red rule and a tinted ground read
   before anyone gets to the sentence underneath. */
.enquiry .freeform-errors,
.enquiry .field__error{
  list-style:none;margin:.5rem 0 0;padding:0;
  font-family:var(--display);font-size:.78rem;font-weight:500;
  letter-spacing:.04em;line-height:1.45;color:var(--royale-ink);
}
.enquiry .freeform-errors li{margin:0}
.enquiry .freeform-errors li + li{margin-top:.2rem}
.enquiry :where(input,select,textarea).freeform-has-errors,
.enquiry :where(input,select,textarea).has-error{
  border-color:var(--royale-ink);
  background:rgba(196,48,29,.07);
}
.enquiry :where(input,select,textarea).freeform-has-errors:focus,
.enquiry :where(input,select,textarea).has-error:focus{
  border-color:var(--royale-ink);
  background:rgba(196,48,29,.11);
}
/* the label above a failed field goes red too, so the eye lands on the pair */
.enquiry .field:has(.freeform-has-errors) > label,
.enquiry .field:has(.has-error) > label{color:var(--royale-ink)}
/* the banner, whichever name it arrives under */
.enquiry .freeform-form-errors,
.enquiry .freeform-form-success{
  margin:0 0 1.3rem;padding:.9rem 1.05rem;
  border:1px solid currentColor;font-size:.95rem;
}
.enquiry .freeform-form-errors{color:var(--royale-ink);background:rgba(196,48,29,.06)}
.enquiry .freeform-form-errors ul{list-style:none;margin:.5rem 0 0;padding:0}
.enquiry .freeform-form-success{color:var(--fg);background:rgba(36,29,18,.05)}
.enquiry .freeform-form-errors p,
.enquiry .freeform-form-success p{margin:0}
/* A checkbox's label is a sentence, not a field name, so it does not take the
   uppercase letterspaced treatment the other labels get — and Freeform wraps
   the input inside the label, so the box has to be pulled out of the
   full-width input rule as well. accent-color tints the native control rather
   than rebuilding it in markup the editor would have to know about. */
.enquiry [data-field-type="checkbox"] label{
  display:flex;align-items:flex-start;gap:.6rem;margin:0;cursor:pointer;
  font-family:inherit;font-size:.96rem;font-weight:400;line-height:1.45;
  letter-spacing:normal;text-transform:none;color:var(--fg);
}
.enquiry input[type="checkbox"]{
  width:1.05rem;height:1.05rem;flex:0 0 auto;margin:.16rem 0 0;padding:0;
  background:none;border:0;accent-color:var(--royale-ink);
}
.enquiry .is-required::after{content:" *";color:var(--royale-ink)}
.form-note{margin:0 0 1.2rem;padding:.85rem 1rem;border:1px solid var(--hair);font-size:.95rem}
.form-note p{margin:0}
.form-note ul{margin:.5rem 0 0;padding-left:1.1rem}
.form-note--ok{border-color:rgba(36,29,18,.35);background:rgba(36,29,18,.05)}
.form-note--bad{border-color:var(--royale-ink);color:var(--royale-ink)}
.enquiry label{
  display:block;
  font-family:var(--display);font-weight:500;font-size:.72rem;
  letter-spacing:.18em;text-transform:uppercase;color:var(--fg-soft);
  margin-bottom:.5rem;
}
.enquiry input,.enquiry select,.enquiry textarea{
  width:100%;
  background:rgba(36,29,18,.04);
  border:1px solid var(--hair);
  color:var(--fg);
  padding:.9rem 1rem;border-radius:var(--radius);
  font:inherit;font-size:1rem;font-weight:400;
  transition:border-color .3s var(--ease),background .3s var(--ease);
}
.enquiry textarea{resize:vertical;min-height:8rem}
.enquiry :where(input,select,textarea):focus{
  background:rgba(36,29,18,.07);
  border-color:rgba(36,29,18,.5);
}
.enquiry button{margin-top:.6rem}
@media (max-width:639px){.enquiry .field-row{grid-auto-flow:row}}

/* --------------------------------------------------------------------------
   30. UPDATE DETAIL
   -------------------------------------------------------------------------- */
/* a post hero carries a date, not a claim row, so it sits shallower */
.hero--post{min-height:clamp(340px,max(40vh,28vw),760px)}
.hero--post .note p{font-family:var(--display);font-weight:500;font-size:.8rem;letter-spacing:.14em;text-transform:uppercase}
.hero--post .note{max-width:none;width:auto}

/* body copy for a post: the one place long-form prose runs on this site */
.prose{max-width:62ch}
.prose p{font-size:clamp(1.02rem,1.2vw,1.13rem);font-weight:400;line-height:1.68;color:var(--fg-soft)}
.prose p + p{margin-top:1.2rem}
.prose h2{
  margin-top:clamp(2rem,3.4vw,2.8rem);margin-bottom:.9rem;
  font-size:clamp(1.2rem,1.9vw,1.5rem);
}
.prose strong{color:var(--fg);font-weight:700}
.prose a{color:var(--accent);border-bottom:1px solid currentColor}

/* a flyer is a portrait poster — show it whole, on a panel, not cropped */
.post-art img{width:100%;height:auto;background:var(--sand)}
/* A flyer is a designed object, not a photograph: the date, the venue and the
   sponsor sit at the FOOT of it. `.band .row figure img` crops every figure in
   a row to 4:3 cover, which on a 3:4 portrait poster throws away the bottom
   third — and at 0,3,1 it out-specifies a bare `.post-art img`, so this has to
   match its weight rather than just come later. (.turf-diagram solves the same
   problem with !important; this is the same intent, stated as specificity.) */
.band .row figure.post-art img{aspect-ratio:auto;object-fit:contain;height:auto}

/* the listing cards are links; the whole card is the target */
.updates a{display:block;color:inherit}
.updates a h3{transition:color .3s var(--ease)}
.updates a:hover h3{color:var(--accent)}
.updates a img{transition:opacity .3s var(--ease)}
.updates a:hover img{opacity:.88}

/* --------------------------------------------------------------------------
   31. BOOKING MODAL
   TeeItUp sends no X-Frame-Options and no frame-ancestors, so it can be framed
   — which is how the live site already embeds it. The gain is that booking
   stops being a trip off the site: the tee sheet opens over the page and the
   back button still means "back to where I was".
   Every trigger is a REAL link to the real booking URL and the script only
   intercepts the click, so it works with no JS, in a new tab, and for a
   crawler. The iframe's src is not set until first open.
   -------------------------------------------------------------------------- */
.booker{
  position:fixed;inset:0;z-index:210;
  display:grid;grid-template-rows:auto 1fr;
  background:rgba(28,22,13,.92);
  opacity:0;visibility:hidden;
  transition:opacity .25s var(--ease),visibility .25s var(--ease);
}
.booker[data-open="true"]{opacity:1;visibility:visible}

.booker__bar{
  display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:.85rem clamp(.9rem,3vw,1.6rem);
  background:var(--char);color:var(--cream);
}
@media (max-width:559px){
  /* three things in a 390px bar: tighten the label rather than dropping it,
     since "open in a new tab" is more useful on a phone, not less */
  .booker__bar{gap:.6rem;padding-inline:.85rem}
  .booker__bar strong{font-size:.72rem;letter-spacing:.14em}
  .booker__out{font-size:.66rem;letter-spacing:.1em;margin-right:.1rem}
}
.booker__bar strong{
  font-family:var(--display);font-weight:500;font-size:.78rem;
  letter-spacing:.2em;text-transform:uppercase;color:var(--sand);
}
.booker__out{
  font-family:var(--display);font-weight:500;font-size:.72rem;
  letter-spacing:.14em;text-transform:uppercase;color:var(--sand);
  margin-left:auto;margin-right:.4rem;
  border-bottom:1px solid rgba(220,208,168,.4);padding-bottom:.1rem;
}
.booker__out:hover{color:var(--cream);border-bottom-color:var(--cream)}
.booker__close{
  width:2.3rem;height:2.3rem;flex:none;display:grid;place-items:center;
  background:none;border:1px solid rgba(242,237,215,.4);border-radius:50%;
  color:var(--cream);cursor:pointer;
  transition:border-color .3s var(--ease),background .3s var(--ease);
}
.booker__close:hover{border-color:var(--cream);background:rgba(242,237,215,.12)}
.booker__close svg{width:13px;height:13px;display:block}

.booker__frame{position:relative;min-height:0;background:var(--cream)}
.booker__frame iframe{width:100%;height:100%;border:0;display:block}
/* the tee sheet takes a moment to boot; say so rather than showing a blank */
.booker__frame::before{
  content:"Loading the tee sheet\2026";
  position:absolute;inset:0;display:grid;place-items:center;
  font-family:var(--display);font-weight:500;font-size:.78rem;
  letter-spacing:.2em;text-transform:uppercase;color:var(--fg-soft);
  pointer-events:none;
}
.booker[data-ready="true"] .booker__frame::before{display:none}

/* Full screen on a phone — a booking flow with a date picker and a tee sheet
   has no room to spare. On a larger screen it insets so the page stays visible
   behind it and closing feels like stepping back rather than navigating. */
@media (min-width:900px){
  /* Wide. The tee sheet lays out a week of dates across the top and a column
     of times under each — squeezing it into a 1100px box made a booking flow
     scroll sideways inside a modal, which is two scrollbars too many. The
     inset is just enough to keep the page visible behind it. */
  .booker{padding:clamp(1rem,3vh,2.25rem) clamp(1rem,2.5vw,2.5rem)}
  .booker__bar{border-radius:var(--radius) var(--radius) 0 0}
  .booker__frame{border-radius:0 0 var(--radius) var(--radius);overflow:hidden}
  .booker > *{max-width:1680px;width:100%;margin-inline:auto}
}
html.bk-open,html.bk-open body{overflow:hidden}
.menu-head{margin-bottom:clamp(2rem,3.5vw,3rem)}
.menu-inset{margin:0}
.menu-inset img{width:100%;height:auto;display:block;background:var(--sand)}
@media (max-width:1023px){.menu-inset{margin-top:1.6rem}}
.band-embed{width:100%;border:0;display:block}
@media (max-width:767px){}
@media (max-width:1023px){}
.events-photo{margin:clamp(2.4rem,4vw,3.4rem) 0 0}
.events-photo img{width:100%;height:auto;display:block;background:var(--sand)}
.frame__scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
@media (max-width:1023px){
.scorecard caption{position:sticky;left:0}
.scorecard th[scope="row"]{position:sticky;left:0;z-index:2;background:var(--bg);box-shadow:1px 0 0 var(--hair)}
.scorecard thead th:first-child{position:sticky;left:0;z-index:3;background:var(--char);box-shadow:1px 0 0 rgba(242,237,215,.25)}
.theme-shell .scorecard th[scope="row"]{background:var(--shell)}
.theme-sand .scorecard th[scope="row"]{background:var(--sand)}
.theme-cream .scorecard th[scope="row"]{background:var(--cream)}
}
@media (max-width:767px){.scorecard--stack{min-width:0}.scorecard--stack caption{display:block;position:static;padding-bottom:.6rem}.scorecard--stack thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.scorecard--stack,.scorecard--stack tbody,.scorecard--stack tr,.scorecard--stack th,.scorecard--stack td{display:block;width:auto}.scorecard--stack tbody tr{border-bottom:1px solid var(--hair);padding:.9rem 0 1rem}.scorecard--stack tbody tr:last-child{border-bottom:0}.scorecard--stack th[scope="row"]{position:static;background:none;box-shadow:none;border:0;padding:0 .9rem .45rem;font-size:1rem;color:var(--fg);white-space:normal}.scorecard--stack td{border:0;padding:.2rem .9rem;text-align:left;display:flex;gap:1rem;align-items:baseline;justify-content:space-between}.scorecard--stack td[data-label]::before{content:attr(data-label);font-size:.7rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:500}}
.frame__scroll{background:linear-gradient(to right,var(--bg) 30%,rgba(255,255,255,0)) 0 0 / 2.4rem 100% no-repeat local,linear-gradient(to left,var(--bg) 30%,rgba(255,255,255,0)) 100% 0 / 2.4rem 100% no-repeat local,radial-gradient(farthest-side at 0 50%,rgba(43,40,31,.20),rgba(43,40,31,0)) 0 0 / .9rem 100% no-repeat scroll,radial-gradient(farthest-side at 100% 50%,rgba(43,40,31,.20),rgba(43,40,31,0)) 100% 0 / .9rem 100% no-repeat scroll;background-color:var(--bg)}
.frame__scroll > .scorecard{background:transparent}
@media (max-width:767px){.frame__scroll:has(.scorecard--stack){background:var(--bg)}}
.scorecard-caption{margin:0;padding:.75rem .9rem .7rem;font-family:var(--display);font-size:.74rem;font-weight:500;letter-spacing:.24em;text-transform:uppercase;color:var(--accent);border-bottom:1px solid var(--hair)}
/* --------------------------------------------------------------------------
   31. PROSE BAND
   -------------------------------------------------------------------------- */
/* .prose was written for a post body — paragraphs, the odd h2, bold, links —
   and a policy needs two things a post never did: section headings, and
   lists. The lists are the real gap. The reset takes every marker off
   (ul{list-style:none}) because every other list on this site is a designed
   component, and a privacy policy that is half bullet points reads as one
   unbroken run of fragments without them.

   All of it is scoped to .prose, so a post body gets the same treatment and
   nothing outside running text moves. h3 and h4 keep the sizes and the face
   _typography gives them; only the spacing is set here. */
.band--prose .band-head{margin-bottom:clamp(1.6rem,3vw,2.4rem)}

.prose h3{margin-top:clamp(2.2rem,3.6vw,3rem);margin-bottom:.8rem}
.prose h4{margin-top:1.7rem;margin-bottom:.5rem}
.prose > :first-child{margin-top:0}

.prose ul,
.prose ol{margin-top:.9rem;padding-left:1.35em;color:var(--fg-soft)}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}
.prose li{font-size:clamp(1.02rem,1.2vw,1.13rem);font-weight:400;line-height:1.6;padding-left:.2em}
.prose li + li{margin-top:.45rem}
.prose li::marker{color:var(--accent)}

/* a paragraph after a list needs the same air as one after a paragraph */
.prose ul + p,
.prose ol + p{margin-top:1.2rem}
@media (max-width:759px){.note p,.prose p,.prose li,.jobs-empty{font-size:1.09rem;line-height:1.62}.card p,.updates p,.game p,.diary p,.menu-list p,.faq .answer p{font-size:1.01rem;line-height:1.58}.card-note,.scorecard-note,.mapbox p{font-size:.95rem}.specs span,.benefits li,.hours dt,.hours__row--closed dd,.stat span,.scorecard--stack td{font-size:1rem}}
@media (min-width:760px) and (max-width:1100px){.hero__bg::after{background:linear-gradient(94deg,rgba(242,237,215,.96) 0%,rgba(242,237,215,.94) 42%,rgba(242,237,215,.86) 62%,rgba(242,237,215,.6) 78%,rgba(242,237,215,.18) 90%,rgba(242,237,215,0) 100%),linear-gradient(180deg,rgba(242,237,215,.5) 0%,rgba(242,237,215,0) 18%)}}
