/* ============================================================================
   NOOKLY V2 DESIGN SYSTEM. Phase 0, 9 August 2026.

   This is the SHIPPED V2 token layer and primitive set. It replaces nothing:
   V1's stylesheets are untouched and every V1 screen still renders exactly as
   it did. A surface becomes V2 by opting in to these classes, one surface at a
   time, which is the whole migration strategy.

   🔴 EVERY CLASS IS PREFIXED `nk-`, AND THAT IS NOT DECORATION. app.html carries
      its own 3000-line stylesheet with names like .card, .btn, .chip, .filters
      and .cats. When a merchant or customer screen migrates, both stylesheets
      will be in the same document for the length of that phase. An unprefixed
      .card here would silently restyle 29 V1 screens the moment this file was
      linked. Do not add an unprefixed class to this file.

   ⚠️ THE FONTS ARE NOT IMPORTED HERE. The page must carry the Google Fonts
      <link> itself, because _headers allows exactly two font origins:
        style-src ... https://fonts.googleapis.com
        font-src  'self' https://fonts.gstatic.com
      Fraunces and Plus Jakarta Sans both come from there, so V2 needs no CSP
      change. Any other font family does, and a CSP change is darkraven's call.

   ⚠️ WHEN app.html MIGRATES, RECONCILE THE RESET ONCE. The reset below is the
      same shape as the one at the top of app.html's <style>, so having both in
      one document is harmless today. If either one changes, they stop agreeing
      and the difference will show up as spacing drift on half a screen.

   THE PALETTE BRIEF, in darkraven's words, 9 Aug: predominantly white, warm
   cream/off-white environmental backgrounds, muted faded sage secondary
   surfaces, deep forest green primary actions and important anchors, restrained
   warm accent only where useful.
   🔴 THAT IS A REAL CHANGE FROM THE 1 AUGUST DRAFT, which set the page
      background to cream and used green bands generously. Here white is the
      page and cream is a band you step into. --nk-green is V1's brand green,
      byte for byte, so V1 and V2 remain one product.

   CUSTOMER CALM. MERCHANT CONTROL.
   ============================================================================ */

:root{
  /* ---------- colour ---------------------------------------------------------
     Three greens and no more: a surface you can put text on (sage), an action
     (green), and a dark band (green-deep). A fourth green is how a palette
     starts to look accidental. */
  --nk-white:#FFFFFF;
  --nk-bg:#FFFFFF;              /* the page. Predominantly white, per the brief */
  --nk-warm:#F8F5EE;            /* warm cream: the environmental band */
  --nk-warm-2:#FCFAF5;          /* softer cream, for a card on cream */
  --nk-sage:#E9F0E6;            /* muted faded sage: secondary surface */
  --nk-sage-2:#DBE7D7;          /* sage border and hover */
  --nk-sage-ink:#37624A;        /* text and icons ON sage. 5.1:1 on --nk-sage */
  --nk-green:#1F5130;           /* 🔴 V1's brand green, unchanged on purpose */
  --nk-green-deep:#14301E;      /* hover, dark bands, merchant sidebar */
  --nk-green-tint:#F3F7F1;      /* barely-there wash, for a hovered row */

  /* ---------- the merchant rail, measured off the approved mockup --------------
     🔴 A FOURTH AND FIFTH GREEN, AND THE PALETTE RULE ABOVE SAYS THERE SHOULD NOT BE.
        They are here because the approved merchant reference became the visual
        specification on 24 August, and it does NOT use --nk-green-deep for its rail.
        Sampled across the mockup the rail ground is #263E32, a lighter and greyer
        green than #14301E, and the active destination is a muted olive rather than
        the pale sage C1 approximated it with before the images were in the repo.
     ⚠️ --nk-rail-active IS NOT THE MEASURED VALUE, and the four point difference is
        deliberate. The mockup's pill is #6E8563, which carries white text at 4.04:1
        and fails AA for a label somebody reads on every screen. #647A5A is 4.69:1,
        passes, and is indistinguishable from it side by side. Measured, not guessed.
     ⚠️ These two are for the RAIL ONLY. Nothing on a content surface may use them, or
        the "three greens" rule stops meaning anything at all. */
  --nk-rail:#263E32;            /* the merchant rail ground */
  --nk-rail-active:#647A5A;     /* the active destination on it, white text at 4.7:1 */

  --nk-ink:#16201A;             /* headings and primary text */
  --nk-ink-2:#3B473F;           /* body text */
  /* 🔴 P2-E/F17. DARKENED 7% ON 24 AUGUST, AND THE OLD COMMENT WAS TRUE BUT INCOMPLETE.
     #6A7568 does clear AA on WHITE at 4.82:1, which is what "4.6:1 on white" was recording.
     It never cleared it on the warm surfaces this product actually puts meta text on:
        cream band #F8F5EE   4.43:1     off-soft #EEEBE1  4.04:1     sage #E9F0E6  4.15:1
     4.04 is the exact ratio that got #6E8563 rejected for the rail pill during C2, so the
     palette already treats that number as failing.
     #636D61 is the SMALLEST correction that clears 4.5:1 on all of them: the same colour
     scaled to 93% toward black, so the hue is unchanged and the character is preserved.
        white 5.40   cream 4.96   off-soft 4.53   sage 4.64
     ⚠️ CHANGED GLOBALLY RATHER THAN PER SURFACE, on browser evidence and not by preference.
        61 muted text nodes were measured across merchant Home, Insights, the unknown state,
        Venue and Browse, and NOT ONE sat on a dark background, so darkening cannot reduce
        contrast anywhere. Three separate light surfaces failed, so a local override would
        have had to enumerate cream, off-soft, sage and #FCFAF5 across 131 rules.
     ⚠️ V1's `--muted` #5C6B58 is a DIFFERENT token and is not touched. It is already darker
        and passes on every one of these surfaces. */
  --nk-muted:#636D61;           /* meta and captions. AA on white AND on the warm surfaces */
  --nk-line:#E5E1D5;            /* the default border, warm not grey */
  --nk-line-soft:#F0EDE3;       /* a divider inside a card */

  /* Restrained warm accent. It marks the beta and the merchant ask, nothing
     else. The moment it appears on three unrelated things it stops meaning
     anything. */
  --nk-amber:#C0842B;
  --nk-amber-soft:#FBF1DC;
  --nk-amber-ink:#74490F;

  /* Status. These carry the REAL product vocabulary, see the badge block. */
  --nk-open:#2E6B45;
  --nk-open-soft:#E4F0E5;
  --nk-busy:#A2543F;
  --nk-busy-soft:#F6E2DC;
  --nk-soon:#8A5A16;
  --nk-soon-soft:#FBEED6;
  --nk-off:#6A7568;
  --nk-off-soft:#EEEBE1;

  /* ---------- type ----------------------------------------------------------
     Fraunces for display only. It is a warm, slightly literary serif and it
     does the "calm and premium" half of the brief on its own. Plus Jakarta
     Sans stays for everything you actually read, which is what V1 already
     ships, so the voice carries over rather than resetting. */
  --nk-serif:'Fraunces',Georgia,'Times New Roman',serif;
  --nk-sans:'Plus Jakarta Sans',system-ui,-apple-system,'Segoe UI',sans-serif;

  /* One scale, fluid where it needs to be. clamp() is doing the responsive
     type work, which is why there is no font-size in the media queries. */
  --nk-fs-display:clamp(37px,5.1vw,61px);
  --nk-fs-h1:clamp(29px,3.3vw,41px);
  --nk-fs-h2:clamp(23px,2.5vw,31px);
  --nk-fs-h3:19px;
  --nk-fs-h4:16px;
  /* 🔴 THE APPLICATION TITLE, AND IT IS A SEPARATE SCALE FROM THE ONES ABOVE ON
     PURPOSE. Everything above this line is the MARKETING scale: the landing hero,
     the signed-out bands, the public pages, where type is allowed to be expressive.
     Inside the app a title is a label on a screen somebody uses every day, so it is
     restrained and it is the SAME on every screen. Ratified 21 August 2026.
     ⚠️ It is applied by `.nk`-scoped rules in app.html, never by a bare class,
        because `.pagehead h1` is shared with 30 merchant V1 screens that must keep
        their sans face and their own size. Changing this token moves the customer
        app only.
     28px up to 875px, a fluid ramp through the tablet band, 32px from 1000px up. */
  --nk-fs-apptitle:clamp(28px,3.2vw,32px);
  --nk-fw-apptitle:700;

  --nk-fs-lead:clamp(16px,1.35vw,18.5px);
  --nk-fs-body:15.5px;
  --nk-fs-sm:13.5px;
  --nk-fs-xs:12px;

  /* ---------- spacing ------------------------------------------------------
     A 4px base. Generous whitespace is a brief item, so the section rhythm
     lives here rather than being retyped per page. */
  --nk-1:4px;  --nk-2:8px;   --nk-3:12px;  --nk-4:16px;
  --nk-5:20px; --nk-6:24px;  --nk-7:32px;  --nk-8:40px;
  --nk-9:48px; --nk-10:56px; --nk-12:72px; --nk-16:104px;
  --nk-section:clamp(56px,7vw,104px);   /* vertical air between sections */

  /* ---------- measure -----------------------------------------------------*/
  --nk-page:1180px;             /* the standard content column */
  --nk-narrow:720px;            /* prose and forms: never full width */

  /* ---------- shape -------------------------------------------------------
     Modern rounded corners, but a hierarchy: a control is less round than a
     card, a card is less round than a band. Everything equally round reads as
     a toy, which is the "not childish" half of the brief. */
  --nk-r-xs:8px;
  --nk-r-sm:11px;
  --nk-r:15px;
  --nk-r-lg:20px;
  --nk-r-xl:28px;
  --nk-r-pill:999px;

  /* ---------- elevation ---------------------------------------------------
     Warm-tinted and low alpha. A grey shadow on a cream page reads as dirt. */
  --nk-sh-sm:0 1px 2px rgba(20,48,30,.05),0 2px 8px rgba(20,48,30,.04);
  --nk-sh:0 2px 4px rgba(20,48,30,.04),0 10px 28px rgba(20,48,30,.06);
  --nk-sh-lg:0 4px 8px rgba(20,48,30,.05),0 22px 56px rgba(20,48,30,.10);

  --nk-ring:0 0 0 3px rgba(31,81,48,.18);   /* focus and selected state */
  --nk-t:.16s cubic-bezier(.4,0,.2,1);      /* one transition, used everywhere */
}

/* ============================================================================
   RESET. See the header note about reconciling this when app.html migrates.
   ============================================================================ */
/* 🔴 EVERY ELEMENT SELECTOR IN THIS RESET IS WRAPPED IN :where(), AND THAT IS NOT
   STYLE. IT IS THE BUG THIS BLOCK SHIPPED WITH FOR TWENTY MINUTES.
   `.nk button` is specificity (0,1,1). `.nk-btn` is (0,1,0). So the reset BEAT the
   component: every <button class="nk-btn"> took `background:none` and
   `color:inherit` from here and rendered as dark text on nothing. The Search
   button in the hero was invisible, and so was the text of every green button on
   the page.
   ⚠️ jsdom did not care and could not: 195 assertions were green while the primary
      action on the landing page had no background. A real browser found it in one
      screenshot. This is trap 19 again, and it is the reason a phase is not done
      until somebody has looked at it.
   :where() contributes ZERO specificity, so each rule below is (0,1,0) from `.nk`
   alone and loses to any component class that comes after it, which is what a
   reset is supposed to do. Do not remove a :where() to "simplify" this. */
.nk *,.nk *::before,.nk *::after{box-sizing:border-box}
.nk{margin:0;padding:0;background:var(--nk-bg);color:var(--nk-ink-2);
  font-family:var(--nk-sans);font-size:var(--nk-fs-body);line-height:1.6;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  /* 🔴 BOTH HALVES ARE LOAD BEARING. A single over-wide child on a phone
     scrolls the whole document sideways, and darkraven's quality rules name
     "no horizontal page overflow" explicitly. overflow-x on the body alone is
     not enough in Safari without the width. */
  overflow-x:hidden;width:100%}
.nk :where(h1,h2,h3,h4,p,figure,ul,ol){margin:0;padding:0}
.nk :where(ul,ol){list-style:none}
.nk :where(a){color:inherit;text-decoration:none}
.nk :where(img,svg){display:block;max-width:100%}
.nk :where(button){font-family:inherit;font-size:inherit;cursor:pointer;border:0;
  background:none;color:inherit}
.nk :where(input,select,textarea){font-family:inherit;font-size:inherit;color:inherit}

/* Keyboard focus is visible everywhere, and only for keyboards. The a11y pass
   on V1 established this; V2 does not get to regress it. */
.nk :focus-visible{outline:2px solid var(--nk-green);outline-offset:2px;border-radius:var(--nk-r-xs)}

/* Screen-reader-only text. Used for the real heading of a section whose visible
   heading is an image or an eyebrow, so the document outline stays correct. */
.nk-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0}

/* Skip link. It exists because the V2 header is sticky and has six targets
   before the content starts. */
.nk-skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--nk-green);
  color:#fff;padding:12px 18px;border-radius:0 0 var(--nk-r-sm) 0;font-weight:700}
.nk-skip:focus{left:0}

/* ============================================================================
   TYPOGRAPHY PRIMITIVES
   ============================================================================ */
.nk-display,.nk-h1,.nk-h2,.nk-h3{font-family:var(--nk-serif);color:var(--nk-ink);
  /* Fraunces has an optical-size axis. 'opsz' at the display end tightens the
     serifs so a 60px headline does not read as ink-heavy. */
  font-variation-settings:'SOFT' 0,'WONK' 0;letter-spacing:-.018em;font-weight:600}
.nk-display{font-size:var(--nk-fs-display);line-height:1.03}
.nk-h1{font-size:var(--nk-fs-h1);line-height:1.1}
.nk-h2{font-size:var(--nk-fs-h2);line-height:1.15}
.nk-h3{font-size:var(--nk-fs-h3);line-height:1.3;letter-spacing:-.01em}
.nk-h4{font-family:var(--nk-sans);font-size:var(--nk-fs-h4);font-weight:700;
  color:var(--nk-ink);line-height:1.4;letter-spacing:-.005em}

.nk-lead{font-size:var(--nk-fs-lead);line-height:1.6;color:var(--nk-ink-2);max-width:56ch}
.nk-body{font-size:var(--nk-fs-body);color:var(--nk-ink-2)}
.nk-sm{font-size:var(--nk-fs-sm);color:var(--nk-muted);line-height:1.55}
.nk-xs{font-size:var(--nk-fs-xs);color:var(--nk-muted);line-height:1.5}
.nk-strong{color:var(--nk-ink);font-weight:700}

/* The green accent inside a headline. One per headline at most. */
.nk-hl{color:var(--nk-green)}

/* Eyebrow: the small sage pill above a heading. It is a label, not a heading,
   so it never carries the section's real <h2>. */
.nk-eyebrow{display:inline-flex;align-items:center;gap:var(--nk-2);
  font-size:var(--nk-fs-xs);font-weight:700;letter-spacing:.01em;
  color:var(--nk-sage-ink);background:var(--nk-sage);
  border-radius:var(--nk-r-pill);padding:6px 13px}
.nk-eyebrow svg{width:14px;height:14px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.nk-eyebrow--onDark{background:rgba(255,255,255,.14);color:#fff}
.nk-eyebrow--amber{background:var(--nk-amber-soft);color:var(--nk-amber-ink)}
/* On a sage BAND the sage pill disappears into its own background, so it lifts to
   white instead. This exists as a variant rather than an inline override because
   every sage band that ever carries an eyebrow needs it. */
.nk-eyebrow--onSage{background:rgba(255,255,255,.62);color:var(--nk-green)}

/* Section head: eyebrow, heading, one line of support, and an optional link on
   the right. Every V2 section uses this, which is what stops nine sections
   inventing nine heading treatments. */
.nk-sechead{display:flex;align-items:flex-end;gap:var(--nk-6);
  margin-bottom:var(--nk-7);flex-wrap:wrap}
.nk-sechead__text{flex:1;min-width:min(100%,300px)}
.nk-sechead .nk-eyebrow{margin-bottom:var(--nk-3)}
.nk-sechead .nk-lead{margin-top:var(--nk-3)}
.nk-sechead__end{margin-left:auto;padding-bottom:var(--nk-1)}
/* ⚠️ POLISH-B: A CENTRED LEAD READS SHORTER THAN A RAGGED-RIGHT ONE. Centred text
   gives the eye no fixed left edge to return to, so the same measure that is
   comfortable ranged left is tiring in the middle of the page. 48ch against the
   56ch a left-aligned lead keeps. Measured at 1440: 758px became 640px. */
.nk-sechead--center{flex-direction:column;align-items:center;text-align:center}
.nk-sechead--center .nk-lead{margin-left:auto;margin-right:auto;max-width:48ch}
.nk-sechead--center .nk-sechead__end{margin-left:0}

/* A link that reads as an action without being a button. */
.nk-link{display:inline-flex;align-items:center;gap:7px;font-weight:700;
  font-size:var(--nk-fs-sm);color:var(--nk-green);transition:var(--nk-t)}
.nk-link svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;transition:var(--nk-t)}
/* ⚠️ POLISH-H: "Browse all" and "View spaces" were 22px tall. They are real
   destinations, not links inside a sentence, so they carry a real target. The
   `.nk-link--inline` variant below is the one that lives in running text and it
   is deliberately NOT given one: WCAG exempts a link in a block of text, and a
   44px inline link would break the line box it sits in. */
.nk-link{min-height:44px}
.nk-link--inline{min-height:0}
.nk-link:hover{color:var(--nk-green-deep)}
.nk-link:hover svg{transform:translateX(3px)}
/* Inside a running sentence, where a flex link would break the line box. */
.nk-link--inline{display:inline}

/* ============================================================================
   LAYOUT: page column, sections, bands, bento
   ============================================================================ */
.nk-page{max-width:var(--nk-page);margin:0 auto;padding-left:var(--nk-6);padding-right:var(--nk-6)}
.nk-narrow{max-width:var(--nk-narrow);margin-left:auto;margin-right:auto}
.nk-section{padding-top:var(--nk-section);padding-bottom:var(--nk-section)}
.nk-section--tight{padding-top:var(--nk-8);padding-bottom:var(--nk-8)}

/* A band is a full-bleed change of surface. Three exist and no more: cream for
   an environmental breath, sage for a quiet aside, green for the one moment the
   page speaks to merchants. */
.nk-band--warm{background:var(--nk-warm)}
.nk-band--sage{background:var(--nk-sage)}
.nk-band--green{background:var(--nk-green-deep);color:rgba(255,255,255,.82)}
.nk-band--green .nk-display,.nk-band--green .nk-h1,
.nk-band--green .nk-h2,.nk-band--green .nk-h3,.nk-band--green .nk-h4,
.nk-band--green .nk-strong{color:#fff}
.nk-band--green .nk-lead,.nk-band--green .nk-body{color:rgba(255,255,255,.80)}
.nk-band--green .nk-sm,.nk-band--green .nk-xs{color:rgba(255,255,255,.62)}
.nk-band--green .nk-link{color:#fff}

/* Bento. 🔴 A GRID, NOT A LOOK. The brief is explicit that Bento belongs on
   landing, customer home, merchant overview and summary surfaces, and NOT on
   forms, settings, tables or Activity. Asymmetry comes from the span helpers,
   so a composition is described where it is used rather than by a bespoke grid
   per page. */
.nk-bento{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--nk-4)}
.nk-span-3{grid-column:span 3}
.nk-span-4{grid-column:span 4}
.nk-span-5{grid-column:span 5}
.nk-span-6{grid-column:span 6}
.nk-span-7{grid-column:span 7}
.nk-span-8{grid-column:span 8}
.nk-span-9{grid-column:span 9}
.nk-span-12{grid-column:span 12}

/* Plain equal grids, for a row of cards that are genuinely peers. */
.nk-grid{display:grid;gap:var(--nk-4)}
.nk-grid--2{grid-template-columns:repeat(2,1fr)}
.nk-grid--3{grid-template-columns:repeat(3,1fr)}
.nk-grid--4{grid-template-columns:repeat(4,1fr)}
/* auto-fit: the honest choice when the item count is DATA and not design. The
   landing page has two live hubs today and will have more; a hard 4-up grid
   would leave two ghost columns. */
.nk-grid--auto{grid-template-columns:repeat(auto-fit,minmax(258px,1fr))}

/* ===== POLISH-B: THE SPACE TYPES SIT IN ONE ROW ============================
   26 August 2026, SIMPLIFIED 23 September 2026 when the taxonomy became four.
   🔴 THE DEFECT THIS STILL GUARDS. "One booking, five kinds of space." drew its
      cards through `.nk-grid--auto`. At 1180px the content column is 1132px and a
      258px track fits FOUR times (4x258 + 3x16 = 1080), so the section rendered
      four cards and dropped the fifth into row two on its own, left aligned,
      beside three empty tracks. Four and an orphan.
   🔴 WHY THIS IS NOT `--auto` WITH A DIFFERENT MINIMUM. The count here is DESIGN,
      not data: there are exactly four space types, the four app.html actually
      offers, and a fifth would be a type nobody can book. `--auto`'s own comment
      says it exists for counts that grow. This one cannot, so the composition is
      allowed to be explicit.
   🔴 FOUR TRACKS, FOUR CARDS, NO PINNING. Team Room and Meeting Room became one
      Group Room on 23 Sep 2026, so the count is even and the 3 + 2 centring this
      block used to perform is GONE rather than adapted: cards 4 and 5 were pinned
      to tracks 2-3 and 4-5 to centre an orphan row, and there is no orphan now.
      Every card is one track wide, so all four are the same size by construction.
   ⚠️ THE TABLET AND PHONE ARMS ARE IN THE RESPONSIVE BLOCK at the foot of this
      file, with the rest of the breakpoints, rather than here. */
.nk-grid--kinds{grid-template-columns:repeat(4,1fr)}

.nk-stack{display:flex;flex-direction:column;gap:var(--nk-4)}
.nk-row{display:flex;align-items:center;gap:var(--nk-3);flex-wrap:wrap}
.nk-row--tight{gap:var(--nk-2)}

/* ============================================================================
   BUTTONS
   Deep forest green is the primary action. Ghost is the second choice on a
   light surface. Sage is a third-tier action inside a card. Amber is reserved
   for the merchant ask and the beta, per the accent rule.
   ⚠️ min-height 44px is the iOS tap target, and it is on the base class rather
      than in a media query, because a 34px button is no easier to hit with a
      mouse than with a thumb.
   ============================================================================ */
.nk-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--nk-2);
  min-height:44px;padding:11px 20px;border-radius:var(--nk-r-sm);
  font-family:var(--nk-sans);font-size:var(--nk-fs-sm);font-weight:700;
  letter-spacing:-.005em;white-space:nowrap;border:1.5px solid transparent;
  background:var(--nk-green);color:#fff;transition:var(--nk-t);text-align:center}
.nk-btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;flex:none}
.nk-btn:hover{background:var(--nk-green-deep)}
.nk-btn:active{transform:translateY(1px)}
.nk-btn[disabled],.nk-btn[aria-disabled="true"]{opacity:.55;cursor:not-allowed;transform:none}

.nk-btn--ghost{background:var(--nk-white);color:var(--nk-ink);border-color:var(--nk-line)}
.nk-btn--ghost:hover{background:var(--nk-white);border-color:var(--nk-green);color:var(--nk-green)}
.nk-btn--sage{background:var(--nk-sage);color:var(--nk-sage-ink);border-color:transparent}
.nk-btn--sage:hover{background:var(--nk-sage-2);color:var(--nk-green-deep)}
.nk-btn--amber{background:var(--nk-amber);color:#fff}
.nk-btn--amber:hover{background:var(--nk-amber-ink)}
/* On the green band: white button, and a ghost whose border is white. */
.nk-btn--light{background:#fff;color:var(--nk-green-deep)}
.nk-btn--light:hover{background:var(--nk-warm)}
.nk-btn--onDark{background:transparent;color:#fff;border-color:rgba(255,255,255,.32)}
.nk-btn--onDark:hover{background:rgba(255,255,255,.10);border-color:rgba(255,255,255,.5)}

.nk-btn--lg{min-height:52px;padding:14px 26px;font-size:15px;border-radius:var(--nk-r)}
.nk-btn--sm{min-height:38px;padding:8px 14px;font-size:var(--nk-fs-xs)}
.nk-btn--block{width:100%}
.nk-btn--icon{padding:0;width:44px;min-height:44px}

/* ============================================================================
   INPUTS AND THE SEARCH CONTROL
   ============================================================================ */
.nk-field{display:flex;flex-direction:column;gap:6px;min-width:0}
.nk-label{font-size:var(--nk-fs-xs);font-weight:700;color:var(--nk-muted);
  letter-spacing:.02em;text-transform:uppercase}
.nk-input,.nk-select{width:100%;min-height:44px;padding:11px 14px;
  background:var(--nk-white);border:1.5px solid var(--nk-line);
  border-radius:var(--nk-r-sm);color:var(--nk-ink);transition:var(--nk-t)}
.nk-input::placeholder{color:var(--nk-muted)}
.nk-input:hover,.nk-select:hover{border-color:var(--nk-sage-2)}
.nk-input:focus,.nk-select:focus{outline:none;border-color:var(--nk-green);box-shadow:var(--nk-ring)}
/* The native arrow is kept: a custom one has to be re-drawn for every browser
   and the native control is the accessible one. Padding leaves it room. */
.nk-select{padding-right:var(--nk-8);cursor:pointer}
.nk-help{font-size:var(--nk-fs-xs);color:var(--nk-muted)}
.nk-err{font-size:var(--nk-fs-xs);color:var(--nk-busy);font-weight:600}
.nk-input--bad{border-color:var(--nk-busy)}

/* The search control. 🔴 IT HAS NO FIXED NUMBER OF FIELDS, on purpose. The
   reference mock draws six (Location, Date, Time, Duration, Guests, Search) and
   the product currently supports two of them. Whoever adds a real dimension
   later adds one .nk-search__cell and nothing else moves, and nobody is tempted
   to draw a dead control to fill a gap in a grid. */
.nk-search{display:flex;align-items:stretch;gap:0;background:var(--nk-white);
  border:1px solid var(--nk-line);border-radius:var(--nk-r-lg);
  box-shadow:var(--nk-sh);padding:var(--nk-2);width:100%}
.nk-search__cell{flex:1;min-width:0;display:flex;flex-direction:column;
  justify-content:center;gap:2px;padding:10px 16px;border-right:1px solid var(--nk-line-soft)}
.nk-search__cell:last-of-type{border-right:0}
.nk-search__label{font-size:11px;font-weight:700;color:var(--nk-muted);
  letter-spacing:.04em;text-transform:uppercase}
/* The control inside a cell is borderless: the cell IS the field.
   ⚠️ POLISH-H: 26px WAS THE COMMENT'S OWN UNDOING. The cell reads as the field but
      the SELECT is what receives the tap, and at 26px it was two thirds of the
      touch floor while sitting inside a 46px cell that looked tappable. The cell
      is unchanged; the control now fills it. */
.nk-search__ctl{width:100%;min-height:44px;padding:0;border:0;background:transparent;
  font-size:var(--nk-fs-sm);font-weight:600;color:var(--nk-ink);cursor:pointer}
.nk-search__ctl:focus{outline:none}
.nk-search__cell:focus-within{background:var(--nk-green-tint);border-radius:var(--nk-r-sm)}
.nk-search__go{flex:none;display:flex;align-items:center}
.nk-search__go .nk-btn{border-radius:var(--nk-r);height:100%}

/* ============================================================================
   CARDS
   ============================================================================ */
.nk-card{background:var(--nk-white);border:1px solid var(--nk-line);
  border-radius:var(--nk-r-lg);padding:var(--nk-6);box-shadow:var(--nk-sh-sm)}
.nk-card--flat{box-shadow:none}
.nk-card--warm{background:var(--nk-warm-2)}
.nk-card--sage{background:var(--nk-sage);border-color:transparent}
.nk-card--pad-lg{padding:var(--nk-7)}
.nk-card--pad-0{padding:0;overflow:hidden}
.nk-card--onDark{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.14)}
/* Interactive only where a whole card is genuinely one link. */
a.nk-card,button.nk-card{display:block;width:100%;text-align:left;transition:var(--nk-t)}
a.nk-card:hover,button.nk-card:hover{border-color:var(--nk-sage-2);
  box-shadow:var(--nk-sh);transform:translateY(-2px)}

/* Feature card: icon tile, heading, one sentence, optional chip. */
.nk-feat__icon{width:40px;height:40px;border-radius:var(--nk-r-sm);background:var(--nk-sage);
  color:var(--nk-sage-ink);display:grid;place-items:center;margin-bottom:var(--nk-4)}
.nk-feat__icon svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.nk-feat .nk-h4{margin-bottom:6px}
/* 🔴 POLISH-H, AND THE NUMBER WAS SET TWICE BEFORE IT WAS RIGHT. Measured at 768:
   this card's sentence ran 85 real characters on one line once the grid folded.
   56ch was tried and it was still 84, because at 13.5px one `ch` is 9.87px while
   the average character is 6.58: the unit overstates the measure by half again.
   50ch is 493px, which is about 75 characters, the top of the 45 to 75 band. The
   same 50ch is used for the app's own 13.5px notes, for the same arithmetic. */
.nk-feat .nk-sm{max-width:50ch}

/* Numbered step, for How it works. */
.nk-step__n{width:32px;height:32px;border-radius:var(--nk-r-xs);background:var(--nk-green);
  color:#fff;display:grid;place-items:center;font-weight:800;font-size:var(--nk-fs-sm);
  margin-bottom:var(--nk-4)}

/* Venue card. The photo slot is .nk-ph until real photography exists. */
.nk-venue__body{padding:var(--nk-4) var(--nk-5) var(--nk-5)}
.nk-venue__top{display:flex;align-items:flex-start;gap:var(--nk-3)}
.nk-venue__top .nk-h4{flex:1;min-width:0}
.nk-venue__rate{text-align:right;flex:none}
.nk-venue__rate b{font-family:var(--nk-serif);font-size:18px;color:var(--nk-ink);font-weight:600}
.nk-venue__meta{display:flex;align-items:center;gap:5px;margin-top:3px}
.nk-venue__meta svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;flex:none}
.nk-venue__chips{display:flex;gap:6px;flex-wrap:wrap;margin-top:var(--nk-4)}

/* ============================================================================
   CHIPS
   ============================================================================ */
.nk-chip{display:inline-flex;align-items:center;gap:6px;font-size:var(--nk-fs-xs);
  font-weight:600;color:var(--nk-sage-ink);background:var(--nk-sage);
  border:1px solid transparent;border-radius:var(--nk-r-pill);padding:5px 11px;
  white-space:nowrap;transition:var(--nk-t)}
.nk-chip svg{width:13px;height:13px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;flex:none}
.nk-chip--plain{background:var(--nk-white);border-color:var(--nk-line);color:var(--nk-muted)}
.nk-chip--onDark{background:rgba(255,255,255,.12);color:#fff}
/* A chip that is a real filter control gets a tap target and a chosen state. */
button.nk-chip{min-height:38px;padding:8px 15px;cursor:pointer;font-size:var(--nk-fs-sm)}
button.nk-chip:hover{background:var(--nk-sage-2)}
button.nk-chip[aria-pressed="true"]{background:var(--nk-green);color:#fff}

/* A horizontal strip of chips. 🔴 THE ONE PLACE HORIZONTAL SCROLL IS INTENDED,
   and the brief allows it explicitly. It still must not scroll the page, which
   is what the parent's overflow-x:hidden guarantees. */
.nk-chipstrip{display:flex;gap:var(--nk-2);overflow-x:auto;padding-bottom:var(--nk-1);
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
/* 🔴 THE EDGE SAYS IT CONTINUES, 31 Aug 2026. Measured at 375px on production v85: the
   six type chips are 920px of content in a 335px box, so the strip is doing exactly what
   the comment above says it should, but the last visible chip was cut mid-word against a
   hard edge and read as broken rather than as scrollable.
   ⚠️ A MASK, NOT AN OVERLAY: a positioned gradient would sit over the chips and eat taps
      near the edge. A mask paints nothing and cannot be hit, so every chip keeps its full
      44px target. Both spellings, because Safari still wants the prefix.
   🔴 AND IT IS INSIDE A MEDIA QUERY FOR A MEASURED REASON. Unscoped, this fades the last
      22px of the strip at EVERY width, including widths where the strip does not scroll
      at all and the last chip is fully visible. Swept in a real browser: at 960px the six
      chips end at 926 inside a 940px box, so nothing scrolls and the fade landed on a chip
      that had nowhere to go. It scrolls at every width at or below 940; 900 is used here
      because it is the breakpoint the merchant tab strip already fades at, and it keeps a
      comfortable margin from that 960px case. Above it the edge stays as it was. */
@media(max-width:900px){
  .nk-chipstrip{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 22px),transparent);
    mask-image:linear-gradient(to right,#000 calc(100% - 22px),transparent)}
}
.nk-chipstrip::-webkit-scrollbar{display:none}
.nk-chipstrip>*{flex:none}

/* ============================================================================
   STATUS AND AVAILABILITY BADGES
   🔴 THE VOCABULARY IS THE PRODUCT'S, NOT THE MOCKUP'S. Bookings are Reserved,
      Checked in, Finished, No-show, Cancelled. A seat is open, taken or unknown.
      "unknown" is a real state and it must stay visible: the availability
      lookup can fail, and V1 shows that honestly rather than guessing "open".
   ============================================================================ */
.nk-badge{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:700;
  border-radius:var(--nk-r-pill);padding:4px 10px;white-space:nowrap;
  background:var(--nk-off-soft);color:var(--nk-off)}
.nk-badge__dot{width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.nk-badge--open{background:var(--nk-open-soft);color:var(--nk-open)}
.nk-badge--busy{background:var(--nk-busy-soft);color:var(--nk-busy)}
.nk-badge--soon{background:var(--nk-soon-soft);color:var(--nk-soon)}
.nk-badge--unknown{background:var(--nk-off-soft);color:var(--nk-off)}
.nk-badge--onDark{background:rgba(255,255,255,.14);color:#fff}

/* ============================================================================
   HONEST PLACEHOLDER ART
   🔴 THIS PRIMITIVE EXISTS BECAUSE THERE ARE NO PHOTOS. Checked against
      production on 9 Aug 2026: two published hubs, `photos` empty on both, and
      the same answer through the public REST path that a signed-out visitor
      uses. The reference mock is photography-led throughout, so something has
      to fill that slot, and stock imagery placed where a specific venue's photo
      goes is a picture of a room that hub does not have.
   So: a labelled gradient, exactly the convention V1 already uses for its
   illustrations. The label is not decoration, it is the disclosure.
   ⚠️ WHEN REAL PHOTOGRAPHY ARRIVES, put an <img> in the same slot and drop the
      label. Nothing else about the card changes. That is why this is a slot and
      not a background on the card itself.
   ============================================================================ */
.nk-ph{position:relative;overflow:hidden;background:var(--nk-sage);
  display:grid;place-items:center;aspect-ratio:16/10}
/* 🔴 THE PREFERRED FILL IS A REAL ILLUSTRATION, NOT THE GRADIENT. V1 already
   ships five of them at assets/photos/ (open, cubicle, team as jpg, meeting and
   conf as svg), drawn in one flat palette, and the comment inside meeting.svg
   states the rule this whole primitive follows: "deliberately an illustration
   and not a photograph, because a photo of someone else's meeting room is not
   what the guest is booking." Use those. The gradient tints below are the
   fallback for a slot with no matching illustration, not the default. */
.nk-ph img{width:100%;height:100%;object-fit:cover;display:block}
.nk-ph--tall{aspect-ratio:4/5}
.nk-ph--wide{aspect-ratio:2/1}
.nk-ph--hero{aspect-ratio:auto;height:100%;min-height:340px;border-radius:var(--nk-r-xl)}
.nk-ph__art{width:100%;height:100%;position:absolute;inset:0}
.nk-ph__glyph{position:relative;width:54px;height:54px;stroke:rgba(20,48,30,.30);
  fill:none;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.nk-ph__tag{position:absolute;left:10px;bottom:10px;font-size:10.5px;font-weight:700;
  letter-spacing:.04em;background:rgba(255,255,255,.82);color:var(--nk-sage-ink);
  border-radius:var(--nk-r-pill);padding:3px 9px}
/* Four tints so a row of cards is not four identical rectangles. They key off
   the real space types, so the variety is meaningful rather than random.
   ⚠️ --team AND --meeting BECAME --group, 23 Sep 2026, and the Group Room keeps the
      green-grey the team card had. */
.nk-ph--open{background:linear-gradient(142deg,#E9F0E6,#D6E4D4 62%,#CBDCC9)}
.nk-ph--cubicle{background:linear-gradient(142deg,#EFEDE2,#E2DFD0 62%,#D8D4C2)}
.nk-ph--group{background:linear-gradient(142deg,#E6EEEA,#D3E2DC 62%,#C6D9D2)}
.nk-ph--conf{background:linear-gradient(142deg,#E4EAEF,#D2DCE6 62%,#C4D1DD)}

/* ============================================================================
   MARKETING HEADER
   ============================================================================ */
.nk-head{position:sticky;top:0;z-index:40;background:rgba(255,255,255,.88);
  backdrop-filter:saturate(180%) blur(12px);-webkit-backdrop-filter:saturate(180%) blur(12px);
  border-bottom:1px solid var(--nk-line-soft)}
.nk-head__in{max-width:var(--nk-page);margin:0 auto;padding:0 var(--nk-6);
  min-height:72px;display:flex;align-items:center;gap:var(--nk-7)}
/* 🔴 THE ICON IS THE WHOLE LOCKUP NOW, so this link must carry the 44px touch target the
   wordmark used to give it for free: 34px of icon is under the floor.
   ⚠️ IT IS A PSEUDO-ELEMENT AND NOT min-height, AND THAT IS THE WHOLE POINT. Sizing the
      box to 44px was tried first and it grew the two-row mobile app bar from 92px to
      102px, a real header regression, and on the footer and the merchant rail it also
      stretched the link so justify-content:center parked the icon in the middle of a
      456px column. An absolutely positioned ::after is outside flow: the box stays 34px,
      no header grows, nothing beside it moves, and the hit area is still 44x44. */
.nk-brand{display:flex;align-items:center;flex:none;position:relative}
.nk-brand::after{content:"";position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);width:44px;height:44px}
.nk-brand img{display:block}
.nk-navlinks{display:flex;align-items:center;gap:var(--nk-6);font-size:var(--nk-fs-sm);
  font-weight:600;color:var(--nk-ink-2)}
/* ⚠️ POLISH-H: 44px, MEASURED AND NOT ASSUMED. These were 36px tall at 1440 and
   32px inside the drawer at 390, so every destination in the site's own
   navigation was under the touch floor on the device most people use. The visible
   underline is still 2px and still sits under the text: `align-items:center` on
   the parent keeps the LINK the same shape and only the hit area grows. */
.nk-navlinks a{display:inline-flex;align-items:center;min-height:44px;padding:6px 0;
  transition:var(--nk-t);border-bottom:2px solid transparent}
.nk-navlinks a:hover{color:var(--nk-green);border-bottom-color:var(--nk-sage-2)}
.nk-head__end{margin-left:auto;display:flex;align-items:center;gap:var(--nk-2);flex:none}
/* 🔴🔴 THE IN-NAV AUTH LINK IS HIDDEN AT EVERY WIDTH, AND IT TAKES TWO SELECTORS,
   1 September 2026. DO NOT COLLAPSE THEM BACK INTO ONE CLASS.

   #mobAuth is the old combined "Log in / Sign up" row inside the nav list. v87 put
   BOTH doors in the bar at every width, so it became a third copy of the same pair.
   v87 deleted the rule that made it green and bold and believed that removed it.
   🔴 IT DID NOT. It was never that rule that showed it. This one was written as a
      bare .nk-navlinks__auth, specificity (0,1,0), and it is outranked TWICE:

        .nk-navlinks a                 (0,1,1)  display:inline-flex   wins everywhere
        .nk-navlinks.nk-open a         (0,2,1)  display:flex          wins in the drawer
        .nk-navlinks__auth             (0,1,0)  display:none          never won

      Measured on production v87 with the bug live: at 375 with the drawer open the
      row rendered 327x53 under FAQ, and at 1280 it was a FIFTH nav link, 98x44 at
      x486, beside the Log in and Sign up buttons it duplicates.

   🔴 SO EACH SELECTOR IS ONE CLASS ABOVE THE RULE IT HAS TO BEAT, and neither one
      depends on source order, which is how the .nk-sechead centring bug was made.
      A media query does not change specificity, so both live here, out of any block.
        .nk-navlinks a.nk-navlinks__auth        (0,2,1)  beats (0,1,1)
        .nk-navlinks.nk-open a.nk-navlinks__auth (0,3,1)  beats (0,2,1)
   ⚠️ NO !important AND NO JAVASCRIPT. Hiding it from script would leave the wrong
      cascade in place and paint the row before the script ran.
   ⚠️ THE ELEMENT STAYS IN THE MARKUP ON PURPOSE. index.html rewrites #mobAuth to
      "My account" for a signed-in visitor, and it rewrites #deskAuth the same way.
      #deskAuth is .nk-head__login, which v87 made visible at every width, so the
      account door is still one tap away in BOTH auth states and nothing is lost.
   🔴 tests/test-mobile-polish.js section 8 proves this with getComputedStyle in
      jsdom, not with a source-text search, because a source-text search is exactly
      what let this ship. */
.nk-navlinks a.nk-navlinks__auth{display:none}
.nk-navlinks.nk-open a.nk-navlinks__auth{display:none}
.nk-burger{display:none;width:44px;height:44px;border-radius:var(--nk-r-sm);
  align-items:center;justify-content:center;flex-direction:column;gap:4px}
.nk-burger span{display:block;width:19px;height:2px;border-radius:2px;background:var(--nk-ink);
  transition:var(--nk-t)}
.nk-burger:hover{background:var(--nk-warm)}

/* ============================================================================
   MODAL AND BOTTOM SHEET
   One component, two presentations: a centred dialog on a desktop, a bottom
   sheet on a phone, which the brief asks for by name. The markup does not
   change between them, so nothing has to be re-templated per breakpoint.
   ============================================================================ */
.nk-scrim{position:fixed;inset:0;z-index:60;background:rgba(20,32,26,.44);
  display:flex;align-items:center;justify-content:center;padding:var(--nk-4)}
.nk-modal{background:var(--nk-white);border-radius:var(--nk-r-xl);box-shadow:var(--nk-sh-lg);
  width:100%;max-width:520px;max-height:min(86vh,720px);overflow-y:auto;
  padding:var(--nk-7)}
.nk-modal__head{display:flex;align-items:flex-start;gap:var(--nk-4);margin-bottom:var(--nk-4)}
.nk-modal__head .nk-h3{flex:1}
.nk-modal__foot{display:flex;gap:var(--nk-2);margin-top:var(--nk-6);flex-wrap:wrap}
.nk-modal__foot .nk-btn{flex:1;min-width:140px}
.nk-sheet__grip{display:none}

/* ============================================================================
   EMPTY, ERROR AND LOADING
   🔴 ALL THREE ARE FIRST-CLASS, because two published hubs and a live fetch
      means the interesting states are the ones a mockup never draws. An empty
      state says what would be here and offers the next move; an error state
      says what failed and offers a way past it; neither pretends.
   ============================================================================ */
.nk-empty{text-align:center;padding:var(--nk-9) var(--nk-6);border:1px dashed var(--nk-line);
  border-radius:var(--nk-r-lg);background:var(--nk-warm-2)}
.nk-empty__icon{width:46px;height:46px;border-radius:var(--nk-r);background:var(--nk-sage);
  color:var(--nk-sage-ink);display:grid;place-items:center;margin:0 auto var(--nk-4)}
.nk-empty__icon svg{width:22px;height:22px;stroke:currentColor;fill:none;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round}
.nk-empty .nk-lead{margin:var(--nk-2) auto 0;max-width:44ch}
.nk-empty .nk-btn{margin-top:var(--nk-5)}

/* ⚠️ POLISH-B: A NOTE IS A SENTENCE, SO IT GETS A SENTENCE'S MEASURE. Measured on
   the landing page at 1440: the "Now available in Davao City" note ran the full
   1132px content column at 13.5px, about 157 characters a line, which is roughly
   twice a comfortable read and the reason it scanned as a banner rather than as
   something to be read. The box still spans the column; the TEXT inside it does
   not. `max-width` on the flex child, so the icon stays put. */
.nk-note{display:flex;align-items:flex-start;gap:var(--nk-3);padding:var(--nk-4) var(--nk-5);
  border-radius:var(--nk-r);background:var(--nk-sage);color:var(--nk-sage-ink);
  font-size:var(--nk-fs-sm);line-height:1.55}
.nk-note > span{max-width:62ch}
.nk-note svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;flex:none;margin-top:1px}
.nk-note--amber{background:var(--nk-amber-soft);color:var(--nk-amber-ink)}
.nk-note--bad{background:var(--nk-busy-soft);color:var(--nk-busy)}
.nk-note b{font-weight:700}

/* Skeleton. Sized by the caller so the shape it replaces does not jump when the
   content lands, which is the only reason a skeleton beats a spinner. */
.nk-skel{background:linear-gradient(90deg,var(--nk-line-soft) 25%,#EAE7DC 37%,var(--nk-line-soft) 63%);
  background-size:400% 100%;animation:nk-shimmer 1.3s ease-in-out infinite;
  border-radius:var(--nk-r-sm)}
@keyframes nk-shimmer{0%{background-position:100% 50%}100%{background-position:0 50%}}
.nk-skel--text{height:12px;margin-bottom:8px}
.nk-skel--short{width:45%}
.nk-skel--title{height:18px;width:62%;margin-bottom:12px}
.nk-skel--art{aspect-ratio:16/10;border-radius:0}

/* ============================================================================
   FOOTER
   ============================================================================ */
.nk-foot{background:var(--nk-green-deep);color:rgba(255,255,255,.66);
  padding-top:var(--nk-12);padding-bottom:var(--nk-7)}
.nk-foot__grid{display:grid;grid-template-columns:minmax(240px,1.4fr) 2fr;gap:var(--nk-9)}
.nk-foot__cols{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--nk-6)}
/* 🔴 POLISH-H: h3, NOT h4, AND THE TAG IS WHY. These three column labels followed
   an h2 and skipped a level, which is the one heading defect on this page: a
   screen reader jumping by heading hears the outline break at the footer. Nothing
   VISIBLE changes, because the size, weight and colour are declared here and not
   by the tag. */
.nk-foot h3{font-family:var(--nk-sans);font-size:var(--nk-fs-xs);font-weight:800;
  letter-spacing:.06em;text-transform:uppercase;color:#fff;margin-bottom:var(--nk-4)}
/* ⚠️ POLISH-H: 32px MEASURED, AND THESE ARE REAL DESTINATIONS. Ten links in three
   footer columns, each a page somebody may actually want on a phone. `display:flex`
   rather than `block` so the min-height centres the label instead of stacking
   padding under it, and `align-items:center` keeps the row looking the same. */
.nk-foot__col a{display:flex;align-items:center;min-height:44px;padding:5px 0;
  font-size:var(--nk-fs-sm);color:rgba(255,255,255,.66);transition:var(--nk-t)}
.nk-foot__col a:hover{color:#fff}
.nk-foot__bot{margin-top:var(--nk-9);padding-top:var(--nk-5);
  border-top:1px solid rgba(255,255,255,.12);display:flex;gap:var(--nk-4);
  align-items:center;flex-wrap:wrap;font-size:var(--nk-fs-xs);
  color:rgba(255,255,255,.55)}
.nk-foot__bot .nk-foot__made{margin-left:auto}

/* --- "Powered by EM Labs Corp.", and it is a LOGO, not a sentence ----------
   🔴 THE SUPPLIED ASSET ALREADY CARRIES THE NAME, so the words beside it are
      "Powered by" and nothing more. "Powered by EM Labs Corp." followed by a
      logo that reads "EM Labs Corp." says it twice. The full sentence survives
      for anyone who cannot see the image, as the img's alt text, which is why
      the alt is "EM Labs Corp." and not "EM Labs logo": alt carries MEANING,
      and the meaning here is the company's name.
   🔴 THE LOGO IS NAVY ON TRANSPARENT AND THIS FOOTER IS DARK FOREST GREEN, so
      it sits on a small light plate. That is the brand-safe way round: the
      official colours are never altered, and the plate is what guarantees the
      contrast. Recolouring somebody else's logo is not a polish decision.
   ⚠️ 3.9:1 IS THE ASSET'S REAL ASPECT RATIO, measured from the trimmed art
      (1310x336). height + width are both set so the row cannot reflow while
      the image loads. */
.nk-poweredby{display:inline-flex;align-items:center;gap:8px;
  font-size:var(--nk-fs-xs);color:rgba(255,255,255,.55)}
.nk-poweredby img{display:block;height:22px;width:86px;border-radius:5px}
.nk-foot .nk-poweredby img{background:var(--nk-warm);padding:4px 7px;
  height:26px;width:99px;box-sizing:content-box}

/* ============================================================================
   RESPONSIVE
   Breakpoints, and what each one is actually for:
     1040px  the sidebar-and-content point. Bento drops to halves.
      860px  tablet portrait. Bento becomes one column, nav collapses.
      640px  phone. Single column priority, sheets, chip strips.
      375px  the smallest target darkraven names. Nothing may overflow here.
   🔴 MOBILE IS NOT A SHRUNK DESKTOP, which is a brief item. The rules below
      change composition and behaviour, not only size: the search stacks into
      real stacked fields, the modal becomes a bottom sheet, the nav becomes a
      drawer.
   ============================================================================ */
@media(max-width:1040px){
  .nk-span-3,.nk-span-4,.nk-span-5{grid-column:span 6}
  .nk-span-7,.nk-span-8,.nk-span-9{grid-column:span 12}
  .nk-grid--4{grid-template-columns:repeat(2,1fr)}
  .nk-navlinks{gap:var(--nk-5)}
  .nk-head__in{gap:var(--nk-5)}

  /* ===== POLISH-B: the space types become a natural two-column composition ===
     Same point `.nk-grid--4` folds to two, so the section stays in step with the
     rest of the page rather than keeping a four-up row nothing else has.
     ⚠️ FOUR CARDS FILL TWO ROWS OF TWO EXACTLY, 23 Sep 2026. The rule that centred
        a fifth card on a row of its own is gone with the fifth card; nothing here
        pins a card any more. */
  .nk-grid--kinds{grid-template-columns:repeat(2,1fr)}
}

@media(max-width:860px){
  .nk-span-3,.nk-span-4,.nk-span-5,.nk-span-6,.nk-span-7,.nk-span-8,.nk-span-9{grid-column:span 12}
  .nk-grid--2,.nk-grid--3{grid-template-columns:1fr}
  .nk-foot__grid{grid-template-columns:1fr;gap:var(--nk-7)}

  /* The nav becomes a drawer. It is hidden from assistive tech as well as from
     sight when closed, which display:none gives us for free and a
     transform-offscreen version would not. */
  .nk-burger{display:flex}
  .nk-navlinks{display:none}
  .nk-navlinks.nk-open{display:flex;position:absolute;left:0;right:0;top:100%;
    flex-direction:column;align-items:stretch;gap:0;background:var(--nk-white);
    border-bottom:1px solid var(--nk-line);box-shadow:var(--nk-sh);padding:var(--nk-2) var(--nk-6) var(--nk-5)}
  /* POLISH-H: the drawer rows keep the 44px the base rule gives them; only the
     padding grows, because a drawer row is a full-width tap target. */
  .nk-navlinks.nk-open a{display:flex;align-items:center;min-height:48px;
    padding:14px 0;border-bottom:1px solid var(--nk-line-soft);
    font-size:var(--nk-fs-body)}
  .nk-navlinks.nk-open a:last-child{border-bottom:0}
  /* ⚠️ THE DRAWER DOES NOT REPEAT THE TWO AUTH DOORS, AND THE RULE THAT ENFORCES THAT
     IS NOT HERE. Log in and Sign up are both in the bar at every width, so the in-nav
     row is a third copy; it is hidden by .nk-navlinks.nk-open a.nk-navlinks__auth up
     beside .nk-navlinks a.nk-navlinks__auth, because a hide written down here would
     still have to outrank .nk-navlinks.nk-open a above it and the pair belongs
     together. v87 deleted a green-and-bold rule from this spot and shipped believing
     that hid the row. It did not: read the comment on those two selectors before
     touching either. The drawer keeps what it is for, the section links. */
  .nk-head{position:sticky}
  .nk-head__in{position:relative;min-height:64px}
}

/* 🔴🔴 LOG IN IS BACK IN THE BAR AT EVERY WIDTH, 31 Aug 2026. DO NOT HIDE IT AGAIN
   WITHOUT MEASURING FIRST.

   There used to be a rule here hiding .nk-head__login below 560px, and its comment
   said "brand, two buttons and a burger do not fit" and that V1 had found the number
   the hard way. That was true of the header V1 had. It is not true of this one, and
   the rule outlived the layout it was written for: on a real iPhone the public header
   showed only the mark, Sign up and a hamburger, so a RETURNING user had no visible way
   in and had to open a menu to find Log in.

   🔴 MEASURED IN A REAL BROWSER BEFORE REMOVING IT, with the button forced visible:

        width   brand ends   controls start   gap between
        430     58           190              132
        393     58           153              95
        375     58           135              77
        320     58           80               22

      At 375 the three controls total 250px inside a 375px bar, leaving 125px of slack,
      and the Log in button measures 73x47, comfortably above the touch floor. Nothing
      is shrunk to make this fit, because nothing needed to be.
   ⚠️ THE DRAWER'S OWN "Log in / Sign up" ROW GOES WITH IT, below. With both doors in
      the bar, that row was a third copy of the same two links. */

@media(max-width:640px){
  .nk-page{padding-left:var(--nk-5);padding-right:var(--nk-5)}
  .nk-grid--4{grid-template-columns:1fr}

  /* 🔴🔴 THE FIVE SPACE TYPES BECOME A SWIPE STRIP ON A PHONE, 31 Aug 2026.
     POLISH-B put them one per row here, and the comment below records why the fifth
     card stopped being a special case. One per row is correct as a FALLBACK and it is
     what a browser without scroll-snap still gets, but measured at 375px it made this
     one section 1857px tall, 2.29 screens, for five cards a reader only wants to
     compare. That is the "takes too long to consume" report.
     🔴 IT IS THE SAME FIVE CARDS IN THE SAME DOM ORDER. No carousel, no library, no
        autoplay, no dots, no JavaScript at all: one scroller and scroll-snap. Every
        card is reachable by swipe, by trackpad and by keyboard, and a screen reader
        walks them in the order they are written.
     ⚠️ 86% AND A NEGATIVE MARGIN, so the next card genuinely peeks past the page
        gutter and the strip reads as continuing. The page itself still cannot scroll
        sideways: .nk-page keeps its padding and the html/body overflow-x:hidden above
        is what makes an intentional inner scroller safe.
     ⚠️ THE CARDS ARE NOT SHRUNK INTO CHIPS. They keep their full height and content;
        only the axis changes. */
  .nk-grid--kinds{
    grid-template-columns:none;
    grid-auto-flow:column;
    grid-auto-columns:86%;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
    /* ⚠️ NO NEGATIVE MARGINS. The first attempt pulled the strip out to the page edge
       with margin-inline:-var(--nk-5) so the next card bled past the gutter. It bled
       past the DOCUMENT too: measured immediately after, the page itself gained a
       horizontal scrollbar, which is the one thing this project does not allow. The
       strip lives inside the page gutter instead and the next card still peeks,
       because a card is 86% of the container and the gap is small. */
    scroll-padding-left:0;
  }
  .nk-grid--kinds::-webkit-scrollbar{display:none}
  /* 🔴🔴 EVERY EXPLICIT COLUMN PLACEMENT MUST BE CLEARED, AND MISSING ONE SCRAMBLES THE
     ORDER. When the desktop layout pinned cards by hand (3 + 2 with the 4th at column 2
     and the 5th at column 4), those placements did not simply stop applying under
     grid-auto-flow:column: measured at the time, the FIFTH CARD RENDERED FIRST, at
     offsetLeft 0, with the other four pushed after it, so a reader swiped into the
     middle of the list. Four equal cards need no pinning, so there is one selector to
     reset now instead of three. IF A PIN EVER COMES BACK, IT COMES BACK HERE TOO. */
  .nk-grid--kinds > *{
    grid-column:auto;
    justify-self:stretch;
    width:auto;
    scroll-snap-align:start;
  }
  /* 🔴🔴 :not(.nk-sechead--center), AND THE EXCLUSION IS THE WHOLE FIX, 31 Aug 2026.
     This rule used to be a bare .nk-sechead{align-items:flex-start}. .nk-sechead--center
     sets align-items:center up at its own rule, but BOTH selectors are one class, so they
     carry the SAME specificity and the later one wins on source order. This block sits
     560 lines below it, so on a phone every centred section head quietly lost its
     centring: the column pinned its child to the start, the child shrank to its content
     width, and the unit sat left of centre while its TEXT stayed centred inside itself.
     🔴 MEASURED on the FAQ head at 375px before the fix: the wrapper was 300px wide at
        left 16, centre 166, against a viewport centre of 187.5. Off by 21.5px. The badge
        and the heading agreed with each other perfectly, which is exactly why it read as
        "not quite centred" rather than as broken.
     ⚠️ NO PER-ELEMENT OFFSET AND NO MARGIN HACK. The centred variant simply keeps the
        align-items it already declares; the default head keeps the flex-start this rule
        was written to give it. */
  .nk-sechead:not(.nk-sechead--center){align-items:flex-start}
  .nk-sechead__end{margin-left:0;padding-bottom:0}

  /* 🔴 THE SEARCH STOPS BEING A ROW. Six cells at 375px would be 40px each,
     which is not a small version of the desktop control, it is a broken one.
     Stacked fields with their own labels and a full-width action instead. */
  .nk-search{flex-direction:column;gap:var(--nk-1);padding:var(--nk-3);
    border-radius:var(--nk-r)}
  .nk-search__cell{border-right:0;border-bottom:1px solid var(--nk-line-soft);
    padding:10px 12px}
  .nk-search__cell:last-of-type{border-bottom:0}
  .nk-search__go{padding-top:var(--nk-2)}
  .nk-search__go .nk-btn{width:100%}

  /* The modal becomes a bottom sheet. */
  .nk-scrim{align-items:flex-end;padding:0}
  .nk-modal{max-width:none;border-radius:var(--nk-r-xl) var(--nk-r-xl) 0 0;
    max-height:90vh;padding:var(--nk-5) var(--nk-5) var(--nk-7)}
  .nk-sheet__grip{display:block;width:38px;height:4px;border-radius:2px;
    background:var(--nk-line);margin:0 auto var(--nk-4)}
  .nk-modal__foot{flex-direction:column-reverse}
  .nk-modal__foot .nk-btn{width:100%;flex:none}

  .nk-foot__cols{grid-template-columns:1fr;gap:var(--nk-5)}
  .nk-foot__bot .nk-foot__made{margin-left:0}
  .nk-card{padding:var(--nk-5)}
  .nk-card--pad-lg{padding:var(--nk-5)}
}

/* 375px and below. Nothing new, just less padding, so a long venue name has
   room instead of pushing the card wider than the screen. */
@media(max-width:400px){
  .nk-page{padding-left:var(--nk-4);padding-right:var(--nk-4)}
  .nk-btn{padding:11px 16px}
}

/* Honour the OS setting. The V1 landing page already does this and V2 keeps it:
   every reveal, shimmer and hover translate is decoration. */
@media(prefers-reduced-motion:reduce){
  .nk *,.nk *::before,.nk *::after{animation-duration:.001ms !important;
    animation-iteration-count:1 !important;transition-duration:.001ms !important}
  a.nk-card:hover,button.nk-card:hover{transform:none}
  .nk-link:hover svg{transform:none}
}
