/* Novra Foods — the entry notice.
 *
 * The house asks once, before the site is explored, that the visitor agree to
 * the Terms of Use and the Privacy Notice. index.html carries the markup at
 * the head of <body>; assets/gate.js carries the behaviour; this file is the
 * whole of its appearance.
 *
 * Nothing is inline. vercel.json serves `style-src 'self'` with no inline
 * allowance and no hashes, so a <style> block here would be dropped by the
 * browser and shipped invisible — the exact failure that put an unstyled
 * 404.html into production. scripts/check-site.py blocks a block, a hex, and a
 * redeclared token; every colour below is a token from css/system.css
 * (DESIGN.md), and there is no literal in this file at all.
 *
 * THE DEFAULT STATE IS OPEN, and that is deliberate. assets/gate.js is
 * `defer`, so it cannot run until the document is parsed. If the overlay
 * defaulted to hidden and script had to reveal it, every first-time visitor
 * would read the whole homepage and then have it covered — the notice would
 * arrive after the thing it is meant to precede. So CSS opens it, and
 * `nf-notice-cleared` on <html> takes it away again, and two files add it:
 * assets/gate-ack.js before the first paint when this browser already holds the
 * acknowledgment, and assets/gate.js when the visitor gives it (or as a
 * fallback for the first case, since gate-ack.js can be blocked). css/gate-nojs.css does the same job for a visitor
 * with no JavaScript, for whom that script will never run at all.
 */

/* The scroll lock, on the root element only.
 *
 * The viewport takes its overflow from <html> whenever <html> is not
 * `visible`, so this one declaration is enough and nothing here has to know
 * what css/home.css puts on <body> (`overflow-x: clip`) in order to hand it
 * back — removing the lock is removing a selector match, not restoring a
 * remembered value. `scrollbar-gutter: stable` keeps the classic scrollbar's
 * width reserved while the bar itself is suppressed, so the page underneath
 * does not jump sideways at the moment the notice closes. */
html:not(.nf-notice-cleared) {
  overflow: hidden;
  scrollbar-gutter: stable;
}
.nf-notice-cleared .nf-gate { display: none; }

/* The overlay. `position: fixed`, so it takes no part in the flow and the page
 * beneath it does not move by a pixel; grid + place-items so the panel is
 * centred and can still cap its own height and scroll inside itself. */
.nf-gate {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  padding: clamp(12px, 4vw, 48px);
  overscroll-behavior: contain;
  font-family: var(--font-text);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* The ground: the ink token, quietened. A scrim is the one place a house like
 * this would reach for a new colour; it does not need one — opacity on a token
 * is a token, and DESIGN.md allows no hex outside css/system.css. Fixed rather
 * than absolute so it still covers the viewport when a long panel scrolls. */
.nf-gate::before {
  content: "";
  position: fixed;
  inset: 0;
  background: var(--ink);
  opacity: 0.78;
}

/* A column, so that when the panel is taller than the phone it is on, the four
 * statements are the part that scrolls and the button does not disappear below
 * the fold of a notice the visitor cannot get past without pressing it. At
 * 320x568 the content runs to about 790px; the header and the button hold
 * their place and the middle moves. `overflow-y: auto` stays on the panel as
 * well as the note, as the valve for a viewport so short that even the header
 * and the button do not fit — clipping a legal notice is not an option.
 *
 * The sheet is narrower than it was (30rem against 34rem): the four statements
 * are set at --t-small over a shorter line, which is the measure a note is read
 * at rather than the measure a contract is set at. Every size below is one of
 * the six tokens in css/system.css — this file used to carry eight ad-hoc ones
 * (11px, 11.5px, 12px, 13.5px, 14.5px and a clamp of its own), which is the
 * defect stage one removed from the rest of the page. */
.nf-gate-panel {
  position: relative;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  width: 100%;
  max-width: 30rem;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* Vertical on the room scale, horizontal on the fixed step. --space-l is
   * clamp(48px, 6vw, 96px), which scales with the VIEWPORT while this sheet
   * is pinned at 30rem — so at 1440 it took 86px a side out of a 480px sheet
   * and left a 305px column, which the button below could not fit in.
   * Measured at 1440/1024/768/560/390/320, not chosen. */
  padding: var(--space-l) var(--space-m);
  background: var(--paper);
  border: 1px solid var(--linen-deep);
  text-align: left;
}

.nf-gate-kicker {
  margin: 0 0 var(--space-s);
  font-family: var(--font-text);
  font-size: var(--t-annot);
  letter-spacing: var(--track-annot);
  text-transform: uppercase;
  color: var(--stone-light);
}

.nf-gate-title {
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--t-headline);
  line-height: var(--lh-headline);
  letter-spacing: -0.008em;
  color: var(--ink);
  text-wrap: balance;
}
/* Focus moves here when the notice opens, so a screen reader reads the label of
 * the dialog it has just been put inside. No ring on it, and that is not the
 * usual mistake: this heading carries tabindex="-1", so it is not in the tab
 * order and no visitor can ever navigate to it — there is no keyboard state
 * being hidden. Chromium does match :focus-visible on a programmatic focus with
 * no prior interaction, which drew a 2px box around the heading on load and
 * made it read as an empty text field. Every element a visitor can actually
 * reach — both policy links and the button — keeps its ring. */
.nf-gate-title:focus,
.nf-gate-title:focus-visible { outline: none; }

/* The letterhead rule under the title. It is the house's chapter mark, the same
 * 40px rule css/home.css draws before every chapter name, and it stays: it is
 * not one of the hairlines that came out. Those were between the statements. */
.nf-gate-rule {
  display: block;
  width: 40px;
  height: 1px;
  margin: 0 0 20px;
  background: var(--ink);
}

/* The note. It was an <ol> whose four rows were divided by hairlines and whose
 * first row's rule was suppressed to hide that it was a table — four fenced
 * clauses, which is how a terms-of-service modal reads and not how a house
 * writes to somebody at its door. One flowing note now: four paragraphs, no
 * rules between them, the agreement as the last line.
 *
 * The scroll behaviour is unchanged and load-bearing. This is the flex item
 * that gives way when the panel is taller than the phone, so the button never
 * goes below the fold of a notice that cannot be dismissed any other way.
 * min-height: 0 is what allows it to shrink past its own content. */
.nf-gate-note {
  margin: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  min-height: 0;
}
.nf-gate-note p {
  margin: 0 0 var(--space-s);
  font-size: var(--t-small);
  line-height: var(--lh-body);
  color: var(--stone);
  text-wrap: pretty;
}
.nf-gate-note p:last-child { margin-bottom: 0; }

/* The agreement is the sentence the notice exists for, so it is the one line of
 * the note set in --ink rather than --stone. No rule above it, no box round it. */
.nf-gate-agree { color: var(--ink); }

.nf-gate a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.nf-gate a:hover { color: var(--navy); }

/* Space, not a rule. The border-top here was the fourth hairline in a sheet that
 * is meant to read as one note. */
.nf-gate-actions {
  flex: none;
  margin-top: var(--space-l);
}

/* One button. There is no "decline" beside it because there is nothing to
 * decline into: the two policy links and the browser's own back button are the
 * exits, and a second button that only closed a tab would be theatre.
 *
 * Paper and a border rather than a navy slab. A filled navy block is the one
 * shape on this page that belongs to somebody else's design language; the house
 * sets its actions in ink on paper with a hairline round them, which is what the
 * proof sheet's letter does and what the .coast-corner tab already does on the
 * photograph. It sizes to its sentence rather than spanning the sheet, so it
 * reads as the last line of a letter and not as a call to action. */
.nf-gate-accept {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  min-height: 48px;
  /* Same reason as the panel: a viewport-scaled inset inside a fixed-width
   * sheet overflowed it by 99px at 1440 and by nothing at 768, which is why
   * it looked correct at the width it was drawn at. */
  padding: 0 var(--space-m);
  max-width: 100%;
  border: 1px solid var(--ink);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--t-annot);
  letter-spacing: var(--track-annot);
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
}
.nf-gate-accept:hover { border-color: var(--navy); color: var(--navy); }
.nf-gate-accept:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }

.nf-gate-fine {
  margin: var(--space-s) 0 0;
  font-size: var(--t-annot);
  line-height: var(--lh-small);
  letter-spacing: 0;
  text-transform: none;
  color: var(--stone-light);
  overflow-wrap: anywhere;
}

/* AFTER every rule it narrows, and that is not a formatting preference. A media
 * query adds no specificity, so the plain `.nf-gate-panel { padding }` above
 * would beat an identical declaration inside a query placed higher in the file
 * and the query would do nothing at all while looking correct. That exact
 * mistake shipped on the proof sheet in this branch (commit cecc9dd) and cost a
 * 98px overflow at 320px, so it is worth the paragraph.
 *
 * One block where there were two. The old 420px query existed to shrink five
 * font sizes; the sizes are tokens now and the tokens already hold at a phone
 * width, so only the sheet's padding and the button's shape are left to say —
 * and both belong at the width where the button's sentence stops fitting.
 *
 * 560px is measured, not chosen: 'I agree — enter the house' is about 258px of
 * letterspaced small caps, and below 560 the sheet it is printed on is narrower
 * than that plus its padding. A full-width bar cannot overflow whatever the
 * viewport, and it is the honest shape for the only action in a letter. */
@media (max-width: 560px) {
  .nf-gate-panel { padding: var(--space-m) var(--space-s); }
  .nf-gate-actions { margin-top: var(--space-m); }
  .nf-gate-accept {
    display: flex;
    width: 100%;
    padding: var(--space-s);
    white-space: normal;
  }
}

/* A short fade, and only for a visitor who has not asked for stillness.
 * Declaring it under `no-preference` rather than overriding it under `reduce`
 * means the reduced-motion visitor never has an animation to cancel.
 *
 * This fade is NOT what keeps a returning visitor from seeing the notice. It
 * used to be doing that job by accident, because starting at zero opacity hides
 * an overlay that is painted before the deferred script can take it down — but
 * it is declared under `no-preference`, so a visitor who asked their system for
 * stillness got no fade and saw the notice flash at full opacity. That was
 * reported from the live site on 2026-09-07. assets/gate-ack.js now settles the
 * question before first paint, which is control flow's job and not an
 * animation's. What is left here is a fade, and only a fade. */
@keyframes nf-gate-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: no-preference) {
  /* On the system tokens, like the rest of the site. The literal 180ms/ease-out
   * this replaces was the last off-token motion anywhere in the CSS.
   * The var() fallbacks are not decoration: system.css and this file are two
   * separate stylesheets, and an unresolved custom property invalidates the whole
   * declaration at computed-value time, which would drop the fade entirely. The
   * fallbacks hold it at what this line already did. */
  .nf-gate { animation: nf-gate-in var(--dur-fast, 200ms) var(--ease, ease-out) both; }
}
@media (prefers-reduced-motion: reduce) {
  .nf-gate, .nf-gate-panel { animation: none; transition: none; }
}

/* The notice a visitor with no JavaScript gets instead: the same four
 * statements, condensed, in normal flow at the top of the page, blocking
 * nothing. It lives inside the <noscript> in index.html, so when scripting is
 * on it is never in the document at all and these rules match nothing. */
.nf-notice-static {
  box-sizing: border-box;
  margin: 0;
  padding: clamp(16px, 3vw, 26px) clamp(20px, 5vw, 80px);
  background: var(--panel);
  border-bottom: 1px solid var(--linen-deep);
  font-family: var(--font-text);
  color: var(--stone);
}
.nf-notice-static p {
  margin: 0 0 10px;
  max-width: var(--measure-wide);
  font-size: var(--t-small);
  line-height: var(--lh-body);
  text-wrap: pretty;
}
.nf-notice-static p:last-child { margin-bottom: 0; }
.nf-notice-static-kicker {
  font-family: var(--font-display);
  font-size: var(--t-annot);
  letter-spacing: var(--track-annot);
  text-transform: uppercase;
  color: var(--ink);
}
.nf-notice-static a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}
