/*
 * KFC Aruba storefront. No build step: served as-is from /css/kfc.css.
 *
 * Tokens: "KFC Digital UI Tokens" (vault, KFC global brand portal > Digital Guidelines, 2026-09-19).
 * Where it conflicts with the older print-derived "KFC Brand Rules for Web and Kiosk", the digital
 * spec wins. Values are STATED by KFC unless marked MEASURED (timed from the portal videos) or
 * ESTIMATED (ours; the mobile typescale tab was not captured). See docs/brand-assets-report.md.
 */

/* ------------------------------------------------------------------ fonts (8 faces, self-hosted)
   Files: public/fonts/kfc/ (KFC Standard package, from the brand portal). Serif Heavy Italic's file
   declares weight 700; we register it as 900 so "Heavy" = 900 everywhere. Noto Sans is the
   stated fallback (used when installed; no external font request). */
@font-face { font-family: "Kentucky Fried Sans"; src: url("/fonts/kfc/KentuckyFriedSans-Light.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSans-Light.woff") format("woff"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Kentucky Fried Sans"; src: url("/fonts/kfc/KentuckyFriedSans-Regular.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSans-Regular.woff") format("woff"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Kentucky Fried Serif"; src: url("/fonts/kfc/KentuckyFriedSerif-Light.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSerif-Light.woff") format("woff"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Kentucky Fried Serif"; src: url("/fonts/kfc/KentuckyFriedSerif-LightItalic.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSerif-LightItalic.woff") format("woff"); font-weight: 300; font-style: italic; font-display: swap; }
@font-face { font-family: "Kentucky Fried Serif"; src: url("/fonts/kfc/KentuckyFriedSerif-Regular.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSerif-Regular.woff") format("woff"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Kentucky Fried Serif"; src: url("/fonts/kfc/KentuckyFriedSerif-Italic.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSerif-Italic.woff") format("woff"); font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Kentucky Fried Serif"; src: url("/fonts/kfc/KentuckyFriedSerif-HeavyItalic.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedSerif-HeavyItalic.woff") format("woff"); font-weight: 900; font-style: italic; font-display: swap; }
@font-face { font-family: "Kentucky Fried Stripes"; src: url("/fonts/kfc/KentuckyFriedStripes-Heavy.woff2") format("woff2"), url("/fonts/kfc/KentuckyFriedStripes-Heavy.woff") format("woff"); font-weight: 900; font-style: normal; font-display: swap; }

/* ------------------------------------------------------------------ tokens */
:root {
    /* Colour: core (stated). Brand Red = headings, stripes, CTA fills, text >= 18.66px only.
       Digital Red = red text under 18.66px (4.59:1 on white). Never off-white. */
    --kfc-brand-red: #F5002B;
    --kfc-digital-red: #EB002B;
    --kfc-white: #FFFFFF;
    --kfc-black: #000000;
    --kfc-herbs: #BFE036;   /* black text only */
    --kfc-spices: #FF9B17;  /* black text only */

    /* Greys (stated): secondary info, disabled states, sub-dividers only. */
    --kfc-grey-50: #F9F8F6; --kfc-grey-100: #EEEBE7; --kfc-grey-200: #D7D4CF; --kfc-grey-300: #A2A09B; --kfc-grey-400: #7E7C77;
    --kfc-grey-500: #6E6C67; --kfc-grey-600: #413F3C; --kfc-grey-700: #2D2B29; --kfc-grey-800: #171614; --kfc-grey-900: #0E0D0C;

    /* Status (stated). Light = tint, Main = fill/border/icon, Dark = text on Light. Info: not stated. */
    --kfc-error-light: #FFEEEE; --kfc-error-main: #DB0000; --kfc-error-dark: #AF0000;
    --kfc-success-light: #EEF7EE; --kfc-success-main: #008300; --kfc-success-dark: #006900;
    --kfc-warning-light: #FBF5E5; --kfc-warning-main: #FFBD15; --kfc-warning-dark: #C89411;

    /* Semantic */
    --kfc-canvas: var(--kfc-white);
    --kfc-text: var(--kfc-black);
    --kfc-text-secondary: var(--kfc-grey-500);  /* Grey 400 is 4.17:1 on white: fails AA body text */
    --kfc-text-link: var(--kfc-digital-red);
    --kfc-cta-bg: var(--kfc-brand-red);
    --kfc-cta-bg-pressed: #A82228;               /* sampled from KFC's focus example, not a stated token */
    --kfc-cta-text: var(--kfc-white);
    --kfc-focus: var(--kfc-black);
    /* Control boundary. KFC states Grey 200 for inputs (1.4:1, fails WCAG 1.4.11 non-text 3:1);
       we use Grey 400 (4.17:1) for input/radio/stepper outlines. Cards keep Grey 200. */
    --kfc-control-line: var(--kfc-grey-400);

    /* Type families (Noto Sans = stated fallback) */
    --kfc-font-stripes: "Kentucky Fried Stripes", "Noto Sans", system-ui, sans-serif;
    --kfc-font-serif: "Kentucky Fried Serif", "Noto Sans", system-ui, sans-serif;
    --kfc-font-sans: "Kentucky Fried Sans", "Noto Sans", system-ui, sans-serif;

    /* Spacing: scale of 4 (stated) */
    --kfc-space-1: 4px; --kfc-space-2: 8px; --kfc-space-3: 12px; --kfc-space-4: 16px; --kfc-space-5: 20px;
    --kfc-space-6: 24px; --kfc-space-7: 28px; --kfc-space-8: 32px; --kfc-space-9: 36px; --kfc-space-10: 40px;
    --kfc-space-12: 48px; --kfc-space-14: 56px; --kfc-space-16: 64px; --kfc-space-24: 96px; --kfc-space-32: 128px;

    /* Grid (stated): small 320-599 = 4 cols / 16 gutter / 16 margin */
    --kfc-grid-cols: 4; --kfc-grid-gutter: 16px; --kfc-grid-margin: 16px;
    --kfc-page-max: 1440px;

    /* Radius (stated, by role) */
    --kfc-radius-none: 0px;   /* header, footer, product cards, category tiles */
    --kfc-radius-40: 4px;     /* buttons, inputs */
    --kfc-radius-100: 8px;    /* selectable cards, banners */
    --kfc-radius-200: 16px;   /* dialogs */

    /* Borders (stated) */
    --kfc-border-400: 4px;    /* primary section divider, black */
    --kfc-border-300: 3px;    /* secondary divider, black */
    --kfc-border-100: 1px;    /* tertiary divider / cards / inputs */
    --kfc-focus-width: 2.5px; --kfc-focus-padding: 4px; --kfc-focus-radius: 4px;

    /* Icons (Lucide, stated) */
    --kfc-icon-md: 24px; --kfc-icon-sm: 20px; --kfc-icon-xs: 16px;

    /* Motion: easing (stated) */
    --kfc-ease-standard: cubic-bezier(0.61, 0.05, 0.60, 1.00);
    --kfc-ease-out: cubic-bezier(0.50, 0.00, 0.21, 1.00);
    --kfc-ease-in: cubic-bezier(0.80, 0.00, 0.59, 1.00);
    /* Motion: durations (MEASURED from the portal videos; KFC publishes no duration tokens) */
    --kfc-dur-press: 150ms; --kfc-dur-label-roll: 300ms;
    --kfc-dur-title-line: 250ms; --kfc-stagger-title: 200ms;
    --kfc-dur-body-line: 150ms; --kfc-stagger-body: 120ms;
    --kfc-dur-page: 450ms;  /* measured PLP -> PDP page push; kept for page transitions */
    --kfc-dur-sheet: 280ms; /* product sheet open/close */

    /* Sticky category strip height (MEASURED): tile 56 + label 16 + padding. Used by the
       deep-link scroll offset so the two can never drift apart again. */
    --kfc-strip-h: 122px;

    --tap: 48px;
    /* Primary CTA label size. KFC's Button style is 16px, but white on Brand Red is 4.27:1, which
       passes AA only as LARGE text (>= 18.66px bold). Serif Heavy Italic is bold, so 19px passes. */
    --kfc-cta-size: 19px;
}

@media (min-width: 600px) { :root { --kfc-grid-cols: 8; --kfc-grid-gutter: 20px; --kfc-grid-margin: 40px; --kfc-strip-h: 144px; } }
@media (min-width: 1136px) { :root { --kfc-grid-cols: 12; --kfc-grid-gutter: 20px; --kfc-grid-margin: 80px; } }

/* ------------------------------------------------------------------ THE WEBSITE DESIGN SYSTEM
 *
 * One product, two surfaces.
 *
 * The kiosk's design system lives at the top of public/css/kiosk.css and is pinned by
 * tests/Feature/Kiosk/KioskDesignSystemTest.php. This is that SAME system at website scale, not
 * a second one: the same four-step rhythm, the same single selected treatment, the same
 * image-to-text ratio, the same currency, the same three typographic jobs. Where a value here
 * differs from the kiosk's it differs for exactly one reason — a mouse and a phone are not a
 * finger at arm's length on a low-density panel — and the line says so.
 *
 * The design review that produced this block found the same defect the kiosk review found, on a
 * different surface: not any single control, but that the desktop pages were compressed while
 * the mobile product and checkout screens were vertically heavy, so the two surfaces read as two
 * products. Nothing about the identity changed. Red, black, white, the photography and the
 * display italic are KFC's and are untouched; what this defines is how much room each gets.
 */
:root {
    /* --- rhythm: the kiosk's four steps, written in the website's own scale of 4 --------------
       Kiosk 8 / 16 / 24 / 40 ARE --kfc-space-2 / 4 / 6 / 10. Aliasing them rather than
       redeclaring the numbers is the whole point: there is one scale in the building. */
    --w-gap-tight: var(--kfc-space-2);      /* 8  — inside one control: icon to label, label to price */
    --w-gap-inline: var(--kfc-space-4);     /* 16 — between controls sitting on one line */
    --w-gap-block: var(--kfc-space-6);      /* 24 — between rows of a list, between cards */
    --w-gap-section: var(--kfc-space-10);   /* 40 — between one section of a page and the next */

    /* --- pointer: the ONE deliberate divergence from the kiosk --------------------------------
       --k-tap is 104px because a cabinet is a large low-density panel at arm's length, with no
       hover and no keyboard. A phone or a mouse is neither. 48px is the existing --tap and is
       what a thumb wants; 44px is WCAG 2.5.5 and is the floor for a control that sits INSIDE a
       bar or a row, where 48 would push the bar taller than the content it acts on. */
    --w-tap: var(--tap);                    /* 48 — a standalone tappable thing */
    --w-tap-inline: 44px;                   /* a control inside a bar or a list row */

    /* --- choice: ONE selected treatment, and it is the kiosk's --------------------------------
       Red line, light red fill, and a mark that is present-but-empty when nothing is chosen, so
       an unanswered question looks unanswered rather than absent. Colour alone is not a state.
       The fill is the kiosk's own --k-chosen-fill and is written out exactly once, below. */
    --w-chosen-line: var(--kfc-brand-red);
    --w-chosen-fill: #FFF4F5;
    --w-chosen-width: var(--kfc-border-300);
    --w-chosen-mark: 20px;                  /* the kiosk's 40px mark, at pointer scale */

    /* --- cards: one image-to-text ratio -------------------------------------------------------
       The kiosk fixes a tile's photograph and its body in pixels because its grid is fixed. A
       website grid is fluid, so the same proportion is held as a ratio plus a RESERVED text
       block — which is what makes the name, the price and the action line up across a row
       whatever length of name the POS sends. */
    --w-card-media: 4 / 3;                  /* every product photograph, in a card or on a page */
    --w-card-name: 36px;                    /* two lines of the product name, reserved */
    --w-card-desc: 40px;                    /* two lines of the description, reserved */
    --w-thumb: 56px;                        /* cart and order-summary line thumbnail */

    /* --- type: three faces, three jobs, the same three as the kiosk ---------------------------
         stripes  the category divider only
         serif    HEADINGS, and the primary CTA label — KFC's brand book sets the CTA in this
                  face and Kentucky Fried Sans ships Light and Regular only, so a sans CTA
                  would be a weaker button, not a cleaner one (the kiosk's same exception)
         sans     everything a customer READS rather than scans: product names, descriptions,
                  option labels, prices, form fields, captions

       Display italic on a product name is the single loudest "this is a template" signal there
       is, and it is why the same chicken was COMBO #1 - 2 PC COMBO on the site and
       Combo #1 - 2 Pc Combo on the kiosk. Pinned by WebsiteDesignSystemTest. */
    --w-type-category: 28px;                /* the Stripes category divider */
    --w-type-item: 16px;                    /* a product's name, in a card or a cart line */
    --w-type-money: 16px;                   /* an inline price */
}
@media (min-width: 600px) { :root { --w-type-category: 40px; --w-card-name: 40px; } }
@media (min-width: 1136px) {
    :root {
        --w-type-category: 56px; --w-type-item: 20px; --w-type-money: 18px;
        --w-card-name: 50px; --w-card-desc: 20px; --w-thumb: 64px;
    }
}

/* The mark. Exactly the kiosk's `.k-option-mark` at pointer scale: a box that is ALWAYS in the
   row, empty, so an unanswered question looks unanswered rather than absent; filled with the
   chosen colour when it is answered. It is decoration — the state a screen reader is told is
   `aria-pressed` / `:checked` on the control itself. */
.chosen-mark {
    flex: none; display: inline-flex; align-items: center; justify-content: center;
    width: var(--w-chosen-mark); height: var(--w-chosen-mark);
    border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40);
    background: var(--kfc-white); color: transparent;
}
.chosen-mark svg { width: 14px; height: 14px; }
[aria-pressed="true"] > .chosen-mark, [aria-pressed="true"] .chosen-mark {
    border-color: var(--w-chosen-line); background: var(--w-chosen-line); color: var(--kfc-white);
}

/* ------------------------------------------------------------------ base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--kfc-canvas);
    color: var(--kfc-text);
    font-family: var(--kfc-font-sans);
    font-weight: 300;              /* Body 1: Sans Light 16/24 */
    font-size: 16px;
    line-height: 24px;
    letter-spacing: 0;
    font-synthesis-weight: none;   /* no faux bold: KFC Sans has Light + Regular only */
    -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }
a { color: inherit; }
button, input, select, textarea { font: inherit; color: inherit; }
strong, b { font-weight: 400; }    /* emphasis = Sans Regular against Light body */

/* Focus (stated): 2.5px black ring, 4px padding, 4px radius. White on the black header. */
/* No border-radius here: changing an element's own radius on focus reshapes it. The ring follows the element's radius. */
:focus-visible { outline: var(--kfc-focus-width) solid var(--kfc-focus); outline-offset: var(--kfc-focus-padding); }
.site-header :focus-visible, .cart-bar :focus-visible, .hero :focus-visible { outline-color: var(--kfc-white); }

.visually-hidden {
    position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link { position: absolute; left: 8px; top: -60px; z-index: 50; background: var(--kfc-white); padding: 10px 14px; }
.skip-link:focus { top: 8px; }

/* Layout container on the stated grid: 4/8/12 columns, margins 16/40/80. */
.wrap { max-width: var(--kfc-page-max); margin: 0 auto; padding: 0 var(--kfc-grid-margin); }
.grid { display: grid; grid-template-columns: repeat(var(--kfc-grid-cols), minmax(0, 1fr)); column-gap: var(--kfc-grid-gutter); }

.icon { flex: none; display: inline-block; vertical-align: middle; }
.icon-md { width: var(--kfc-icon-md); height: var(--kfc-icon-md); }
.icon-sm { width: var(--kfc-icon-sm); height: var(--kfc-icon-sm); }
.icon-xs { width: var(--kfc-icon-xs); height: var(--kfc-icon-xs); }

/* ------------------------------------------------------------------ typescale
   DESKTOP values = stated (>= 1136px). Below 1136px = ESTIMATED mobile values (the Mobile tab of
   KFC's typescale was not captured). They use steps from KFC's own scale (10 12 14 16 20 22 24 28
   40 52 ...). Replace when KFC ATLAS DS supplies the mobile tab. */
.kfc-hero-xl, .kfc-hero { font-family: var(--kfc-font-stripes); font-weight: 900; font-style: normal; text-transform: uppercase; letter-spacing: 0; }
.kfc-h1, .kfc-h2, .kfc-h3, .kfc-h4, .kfc-h5, .kfc-button, .kfc-button-sm,
h1, h2, h3, h4, .heading {
    font-family: var(--kfc-font-serif); font-weight: 900; font-style: italic; text-transform: uppercase; letter-spacing: 0; margin: 0;
}
.kfc-subtitle-1, .kfc-subtitle-2, .kfc-body-1, .kfc-body-2, .kfc-caption, .kfc-overline { font-family: var(--kfc-font-sans); font-style: normal; letter-spacing: 0; }

/* mobile (ESTIMATED) */
.kfc-hero-xl { font-size: 52px; line-height: 48px; }
.kfc-hero { font-size: 40px; line-height: 38px; }
.kfc-h1, h1 { font-size: 32px; line-height: 32px; }
.kfc-h2 { font-size: 28px; line-height: 28px; }
.kfc-h3, h2 { font-size: 24px; line-height: 24px; }
.kfc-h4, h3, h4 { font-size: 20px; line-height: 20px; letter-spacing: .25px; }
.kfc-h5 { font-size: 16px; line-height: 16px; letter-spacing: .25px; }
.kfc-subtitle-1 { font-weight: 300; font-size: 16px; line-height: 24px; }
.kfc-subtitle-2 { font-weight: 300; font-size: 14px; line-height: 20px; }
.kfc-body-1 { font-weight: 300; font-size: 16px; line-height: 24px; }
.kfc-body-2 { font-weight: 300; font-size: 14px; line-height: 24px; }
.kfc-caption { font-weight: 300; font-size: 12px; line-height: 16px; }
.kfc-overline { font-weight: 400; font-size: 12px; line-height: 16px; text-transform: uppercase; }
.kfc-button { font-size: 16px; line-height: 24px; }
.kfc-button-sm { font-size: 14px; line-height: 16px; }

@media (min-width: 600px) {  /* ESTIMATED medium step */
    .kfc-hero-xl { font-size: 72px; line-height: 68px; }
    .kfc-hero { font-size: 52px; line-height: 48px; }
    .kfc-h1, h1 { font-size: 40px; line-height: 40px; }
    .kfc-h3, h2 { font-size: 28px; line-height: 28px; }
}
@media (min-width: 1136px) {  /* DESKTOP: stated */
    .kfc-hero-xl { font-size: 96px; line-height: 86px; }
    .kfc-hero { font-size: 72px; line-height: 68px; }
    .kfc-h1, h1 { font-size: 52px; line-height: 52px; }
    .kfc-h2 { font-size: 40px; line-height: 40px; }
    .kfc-h3, h2 { font-size: 32px; line-height: 32px; }
    .kfc-h4, h3, h4 { font-size: 22px; line-height: 22px; }
    .kfc-subtitle-1 { font-size: 18px; line-height: 24px; }
}

/* Category divider: Stripes typeface in Brand Red. It carried the Mega step (52/72/96px), and
   Stripes is a very wide face — "CRAVE MENU" took two lines and a third of a 390px screen
   before the first product. --w-type-category is the step that keeps a category name on ONE
   line at 390 while still reading as the page's biggest red thing. */
.category-title { color: var(--kfc-brand-red); overflow-wrap: anywhere; font-size: var(--w-type-category); line-height: 1; }

p { margin: 0 0 var(--kfc-space-3); }
.muted { color: var(--kfc-text-secondary); }
.small { font-size: 14px; line-height: 20px; }

/* ------------------------------------------------------------------ header: black bar, red lettermark */
.site-header { background: var(--kfc-black); color: var(--kfc-white); }
.site-header .wrap { display: flex; align-items: center; gap: var(--kfc-space-4); min-height: 64px; }
/* Lettermark clear space: 20% of its height on every side (32px mark -> 6.4px, padded to 8px). */
.logo { display: inline-flex; align-items: center; padding: var(--kfc-space-2); margin-left: calc(-1 * var(--kfc-space-2)); text-decoration: none; }
.logo img { height: 32px; width: auto; }
.site-nav { display: flex; align-items: center; gap: var(--kfc-space-1); margin-left: auto; }
.site-nav a.nav-link {
    display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--kfc-space-3);
    color: var(--kfc-white); text-decoration: none; text-transform: uppercase; font-weight: 400; font-size: 14px; letter-spacing: 0;
}
.site-nav a.nav-link:hover, .site-nav a.nav-link[aria-current="page"] { color: var(--kfc-brand-red); }
.site-nav a.nav-desktop { display: none; }
@media (min-width: 600px) { .site-nav a.nav-desktop { display: inline-flex; } }
/* m1: 44px, the WCAG 2.5.8 minimum — the header bar itself stays 64px. */
.site-header .btn { min-height: 44px; padding: 0 var(--kfc-space-4); }

/* ------------------------------------------------------------------ buttons
   Primary: Brand Red fill, white Serif Heavy Italic caps at 19px (large text, see --kfc-cta-size),
   radius 4. Press: scale .97 + label roll (MEASURED from KFC's button video). */
.btn {
    position: relative; overflow: hidden;
    display: inline-flex; align-items: center; justify-content: center; gap: var(--kfc-space-2);
    min-height: var(--tap); padding: 0 var(--kfc-space-5);
    border: 0; border-radius: var(--kfc-radius-40);
    background: var(--kfc-cta-bg); color: var(--kfc-cta-text);
    font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase;
    font-size: var(--kfc-cta-size); line-height: 24px; letter-spacing: 0; text-decoration: none; cursor: pointer;
    transition: transform var(--kfc-dur-press) var(--kfc-ease-out), background-color var(--kfc-dur-press) var(--kfc-ease-out);
}
.btn:focus-visible { background: var(--kfc-cta-bg-pressed); }
@media (hover: hover) { .btn:hover { background: var(--kfc-cta-bg-pressed); } }
.btn:active { transform: scale(.97); background: var(--kfc-cta-bg-pressed); }
.btn.btn[disabled], .btn.btn[aria-disabled="true"], fieldset:disabled .btn.btn {
    background: var(--kfc-grey-100); color: var(--kfc-grey-500); cursor: not-allowed; transform: none;
}
.btn-block { display: flex; width: 100%; }
/* A class selector beats the browser's own [hidden] rule, so .btn-block[hidden] stayed on screen.
   That is what kept the dead grey CONTINUE TO PAYMENT block visible after the payment step opened
   (B4) — the same trap already documented for .cart-bar[hidden] and .progress[hidden]. */
.btn[hidden], .btn-block[hidden] { display: none; }
.btn-large { min-height: 56px; }
/* Secondary: black outline on white, black label (21:1), KFC Button size 16px. */
.btn-outline {
    background: var(--kfc-white); color: var(--kfc-black); box-shadow: inset 0 0 0 1px var(--kfc-black); font-size: 16px;
}
.btn-outline:focus-visible, .btn-outline:active { background: var(--kfc-grey-100); color: var(--kfc-black); }
@media (hover: hover) { .btn-outline:hover { background: var(--kfc-grey-100); color: var(--kfc-black); } }
.btn-count {
    display: inline-block; min-width: 24px; padding: 0 6px; border-radius: var(--kfc-radius-100); background: var(--kfc-white); color: var(--kfc-black);
    font-family: var(--kfc-font-sans); font-style: normal; font-weight: 400; font-size: 14px; line-height: 24px; text-align: center;
}

/* Label roll: motion.js wraps a text-only label in two stacked copies (the copy is aria-hidden). */
.btn-roll { display: inline-grid; overflow: hidden; line-height: inherit; }
.btn-roll > span { grid-area: 1 / 1; transition: transform var(--kfc-dur-label-roll) var(--kfc-ease-standard); }
.btn-roll > span + span { transform: translateY(110%); }
.btn:active .btn-roll > span:first-child { transform: translateY(-110%); }
.btn:active .btn-roll > span + span { transform: none; }
@media (hover: hover) {
    .btn:not([disabled]):not([aria-disabled="true"]):hover .btn-roll > span:first-child { transform: translateY(-110%); }
    .btn:not([disabled]):not([aria-disabled="true"]):hover .btn-roll > span + span { transform: none; }
}

.text-link { display: inline-flex; align-items: center; gap: var(--kfc-space-1); min-height: var(--tap); color: var(--kfc-text-link); font-weight: 400; text-underline-offset: 3px; }
.link-button {
    background: none; border: 0; padding: 0 var(--kfc-space-1); min-height: var(--tap); cursor: pointer; font-weight: 400;
    color: var(--kfc-text-link); text-decoration: underline; text-underline-offset: 3px; font-size: 14px;
}
/* M10 — spec §5, verbatim: functional icons are Black on light and "are never red in the
   functional set". The label stays Digital Red; only the glyph goes black. */
.text-link .icon, .link-button .icon, .store-current .change .icon, .locate-button .icon { color: var(--kfc-black); }

/* ------------------------------------------------------------------ page frame */
main { display: block; }
/* Mobile pages were vertically heavy and desktop ones compressed, so the frame is not one
   number: 20px above the first thing on a phone, the stated 32 from 600px up. */
.page { padding-top: var(--kfc-space-5); padding-bottom: var(--kfc-space-12); }
@media (min-width: 600px) { .page { padding-top: var(--kfc-space-8); padding-bottom: var(--kfc-space-14); } }
.page-head { margin-bottom: var(--kfc-space-6); }
.page-head p { margin-top: var(--kfc-space-3); }

.notice { border-radius: var(--kfc-radius-100); padding: var(--kfc-space-3) var(--kfc-space-4); background: var(--kfc-warning-light); color: var(--kfc-black); margin: 0 0 var(--kfc-space-4); }
.error-text { color: var(--kfc-error-dark); font-weight: 400; }
[role="alert"]:empty { display: none; }

/* ------------------------------------------------------------------ home hero
   One licensed photo (see the photo README next to the image). Short on phones so the store setup and
   the first menu category stay above the fold at 390x844. */
.hero { background: var(--kfc-black); color: var(--kfc-white); overflow: hidden; }
.hero .wrap { max-width: none; }
.hero .wrap { display: grid; grid-template-columns: minmax(0, 1fr) 42%; align-items: center; min-height: 184px; }
.hero-text { padding: var(--kfc-space-6) var(--kfc-space-4) var(--kfc-space-6) 0; }
.hero-title { color: var(--kfc-white); }
.hero-title .hero-line { display: block; }
.hero-title .hero-line-red { color: var(--kfc-brand-red); }
.hero-sub { margin: var(--kfc-space-3) 0 0; font-size: 14px; line-height: 20px; }
.hero-photo { align-self: stretch; margin-right: calc(-1 * var(--kfc-grid-margin)); position: relative; }
/* p1: at 390 the photo panel is narrow enough that 48% cut a face in half at its right edge. */
.hero-photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 35% 30%; }
@media (min-width: 600px) { .hero-photo img { object-position: 48% 30%; } }
@media (min-width: 600px) {
    .hero .wrap { min-height: 280px; grid-template-columns: minmax(0, 1fr) 50%; }
    .hero-sub { font-size: 16px; line-height: 24px; }
}
@media (min-width: 1136px) {
    .hero .wrap { min-height: 440px; }
    .hero-sub { font-size: 18px; }
}

/* ------------------------------------------------------------------ order setup (home)
   P1-4. A distinct, spacious ordering block rather than a row of outlined boxes: its own band
   on the page's own grey, a 4px black rule above it — KFC's primary section divider — a heading
   that says what it is for, and the kiosk's selected treatment on both answers. The band runs
   to the page edges on a phone by taking the grid margin back and paying it again as padding,
   so the block is a band and its contents still sit on the grid. */
.order-setup { padding: var(--w-gap-inline) 0 var(--kfc-space-1); }
.order-block {
    margin: 0 calc(-1 * var(--kfc-grid-margin));
    padding: var(--w-gap-block) var(--kfc-grid-margin) var(--w-gap-block);
    background: var(--kfc-grey-50); border-top: var(--kfc-border-400) solid var(--kfc-black);
}
.order-block-title { margin: 0 0 var(--w-gap-inline); }
.fulfilment { display: grid; grid-template-columns: 1fr 1fr; gap: var(--w-gap-tight); margin: 0; }
.fulfilment button {
    display: flex; align-items: center; justify-content: center; gap: var(--w-gap-tight);
    min-height: 56px; border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-100);
    background: var(--kfc-white); color: var(--kfc-black); cursor: pointer;
    font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase; font-size: 16px;
}
/* Pickup / delivery is the website's version of the kiosk's eat-in / takeaway question, so it
   gets the kiosk's answer: red line, light red fill, a mark — not a black fill, which read as a
   toggle switch in a settings form rather than as a choice that had been made. */
.fulfilment button[aria-pressed="true"] {
    background: var(--w-chosen-fill); border-color: var(--w-chosen-line); border-width: var(--w-chosen-width); color: var(--kfc-black);
}
.fulfilment button[disabled] { color: var(--kfc-grey-500); background: var(--kfc-grey-50); border-color: var(--kfc-grey-200); cursor: not-allowed; }
.fulfilment button[disabled] .chosen-mark { border-color: var(--kfc-grey-200); }

.store-picker > summary { list-style: none; cursor: pointer; }
.store-picker > summary::-webkit-details-marker { display: none; }
/* The current restaurant, as a row inside the block rather than a form field: white, its own
   boundary, and the same 8px radius every selectable card on the site uses. */
.store-current {
    display: flex; align-items: center; gap: var(--kfc-space-3); margin-top: var(--w-gap-inline); min-height: 56px;
    padding: var(--w-gap-tight) var(--kfc-space-3); background: var(--kfc-white);
    border: var(--kfc-border-100) solid var(--kfc-grey-200); border-radius: var(--kfc-radius-100);
}
/* Below 600px the status drops to its own line under the restaurant's name rather than
   competing with it for the same row — the name is the thing being named and it was the thing
   that wrapped. */
@media (max-width: 599px) {
    .store-current { flex-wrap: wrap; row-gap: 2px; }
    .store-current .status { order: 3; flex-basis: 100%; margin-left: calc(var(--kfc-icon-md) + var(--kfc-space-3)); }
}
.store-current .icon { color: var(--kfc-black); }
.store-current .store-text { flex: 1; min-width: 0; }
.store-current .store-label { font-size: 12px; color: var(--kfc-text-secondary); display: block; line-height: 16px; }
.store-current strong { font-size: 16px; line-height: 20px; }
.store-current .change { display: inline-flex; align-items: center; gap: 2px; font-size: 14px; font-weight: 400; color: var(--kfc-text-link); text-decoration: underline; text-underline-offset: 3px; }
/* m7: the picker had no collapse affordance once open — `.change` was hidden outright and the
   chevron never flipped. Same control, swapped label, rotated glyph. */
.store-current .change-close { display: none; }
.store-picker[open] .change-open { display: none; }
.store-picker[open] .change-close { display: inline; }
.store-picker[open] .change .icon { transform: rotate(180deg); }
.store-current .change .icon { transition: transform var(--kfc-dur-press) var(--kfc-ease-out); }
.status { font-size: 14px; font-weight: 400; white-space: nowrap; }
.status-open::before, .status-closed::before {
    content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 6px; vertical-align: 1px; border-radius: 50%;
}
.status-open { color: var(--kfc-success-dark); }
.status-open::before { background: var(--kfc-success-main); }
.status-closed { color: var(--kfc-text-secondary); }
.status-closed::before { background: transparent; border: 1.5px solid var(--kfc-grey-500); }

/* Store tiles = selectable cards: radius 8, 1px Grey 200, selected 1px Grey 900 (+ inset for weight). */
.store-list { list-style: none; margin: var(--kfc-space-3) 0 0; padding: 0; display: grid; gap: var(--kfc-space-2); }
.store-list button {
    width: 100%; height: 100%; text-align: left; cursor: pointer; background: var(--kfc-white);
    border: var(--kfc-border-100) solid var(--kfc-grey-200); border-radius: var(--kfc-radius-100); padding: var(--kfc-space-3) var(--kfc-space-4); min-height: 64px;
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 2px var(--kfc-space-3); align-items: start; align-content: start;
}
.store-list form { height: 100%; }
.store-list button:hover { border-color: var(--kfc-grey-900); }
/* Same chosen treatment again. A 1px grey-900 hairline plus a 1px inset was the whole signal
   that this was the restaurant you were ordering from, which is what made a row of four of them
   read as an administrative form rather than as a choice. */
.store-list button[aria-pressed="true"] {
    border-color: var(--w-chosen-line); border-width: var(--w-chosen-width); background: var(--w-chosen-fill); box-shadow: none;
    padding: calc(var(--kfc-space-3) - 2px) calc(var(--kfc-space-4) - 2px);
}
.store-list .store-name { font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase; font-size: 16px; line-height: 20px; }
.store-list .store-address { grid-column: 1; font-size: 12px; line-height: 16px; color: var(--kfc-text-secondary); }
/* Whether it is open is its own line under the address, at every width. It used to be a
   right-hand cell below 1136px and a third line above it, so the same four tiles said the same
   thing in two different shapes depending on the window. */
.store-list .status { grid-column: 1; margin-top: 2px; }
.store-list .chosen-mark { grid-column: 2; grid-row: 1 / span 3; align-self: center; }
@media (min-width: 600px) { .store-list { grid-template-columns: 1fr 1fr; gap: var(--kfc-grid-gutter); } }
@media (min-width: 1136px) { .store-list { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 600px) {
    .order-setup { padding-top: var(--w-gap-block); }
    .order-block { padding-top: var(--w-gap-section); padding-bottom: var(--w-gap-section); }
    .fulfilment { max-width: 420px; gap: var(--w-gap-inline); }
}

/* Nearest store (store-locate.js): one plain line + a text link, under the picker. */
/* m6: the message and the button that acts on it must stay adjacent. They used to sit at
   opposite ends of a 1136px row — ~1100px apart — because the line took all the free space. */
.store-locate { display: flex; flex-wrap: wrap; align-items: center; column-gap: var(--kfc-space-3); margin-top: var(--w-gap-tight); }
/* The consequence of the choice, not part of making it: its own space, below the block. */
.order-setup > .notice { margin: var(--w-gap-inline) 0 0; }
.nearest-line { margin: 0; flex: 0 1 auto; max-width: 100%; font-size: 14px; line-height: 20px; }
.nearest-line[hidden] { display: none; }
.locate-button { display: inline-flex; align-items: center; gap: var(--kfc-space-1); text-decoration: none; }
.locate-button span, .locate-button { text-underline-offset: 3px; }
.locate-button[hidden] { display: none; }
.deliver-to { display: flex; flex-wrap: wrap; align-items: center; gap: var(--kfc-space-2) var(--kfc-space-3); margin-top: var(--kfc-space-3); }
.deliver-to label { font-weight: 400; font-size: 14px; }
.deliver-to select { flex: 1 1 200px; width: auto; }
@media (min-width: 1136px) { .order-block .store-locate, .order-block .deliver-to { grid-column: 1 / -1; justify-content: flex-start; } }

.login-prompt { display: flex; align-items: center; gap: var(--kfc-space-3); margin-top: var(--kfc-space-3); font-size: 14px; line-height: 20px; }
.login-prompt p { margin: 0; flex: 1 1 0; min-width: 0; }
.login-prompt .btn { flex: none; min-height: 44px; }

@media (min-width: 1136px) {
    .order-block { display: grid; grid-template-columns: 420px 1fr; column-gap: var(--kfc-space-12); row-gap: var(--w-gap-inline); align-items: center; }
    /* The picker's own header is a line of text, not a 1280px-wide bar with its status and its
       Close stranded at opposite ends of the window (the m6 failure, in a different control). */
    .order-block .store-picker > summary { max-width: 720px; }
    .order-block-title { grid-column: 1 / -1; grid-row: 1; margin-bottom: 0; }
    .order-block .fulfilment { grid-column: 1; grid-row: 2; }
    .order-block .login-prompt { grid-column: 2; grid-row: 2; margin-top: 0; justify-content: flex-end; }
    .order-block .login-prompt p { flex: 0 1 auto; }
    .order-block .store-picker { grid-column: 1 / -1; }
    .order-setup .notice { margin-top: var(--kfc-space-4); }
    /* m7: the summary STAYS. menu.js force-opens the picker at >=1100px, so hiding an open
       summary here took away both the Close control and the line naming the restaurant, on
       exactly the screens where the picker is always open. */
    .store-picker[open] .store-list { margin-top: var(--kfc-space-5); }
}

.context-line { display: flex; align-items: center; gap: var(--kfc-space-3); flex-wrap: wrap; padding: var(--kfc-space-3) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); font-size: 14px; }
.context-line .spacer { flex: 1; }

/* ------------------------------------------------------------------ menu: category strip (sticky) */
.menu-head { padding: var(--kfc-space-6) 0 var(--kfc-space-2); }
.category-strip { position: sticky; top: 0; z-index: 20; background: var(--kfc-white); border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
.category-strip ul {
    list-style: none; margin: 0 auto; padding: var(--kfc-space-2) var(--kfc-grid-margin); max-width: var(--kfc-page-max);
    display: flex; gap: var(--kfc-space-1); overflow-x: auto; scrollbar-width: none; scroll-snap-type: x proximity;
    /* M8: without this, scroll-snap-align: start snaps the first tile PAST the ul's own padding
       on load, so it sits flush to the viewport edge while everything else on the page starts at
       the 16px grid margin. */
    scroll-padding-left: var(--kfc-grid-margin);
}
.category-strip ul::-webkit-scrollbar { display: none; }
/* Eight categories on a strip that scrolls sideways, so the width is not scarce: 104px a tile
   and a label at 13px, with two lines RESERVED, rather than eight names squeezed into 92px of
   12px type with some tiles a line taller than their neighbours. */
.category-strip a {
    display: flex; flex-direction: column; align-items: center; gap: var(--kfc-space-1); width: 104px; min-height: var(--tap);
    padding: var(--kfc-space-1) 2px 6px; text-decoration: none; text-align: center; scroll-snap-align: start;
    border-bottom: var(--kfc-border-300) solid transparent; border-radius: 0;
}
/* Category switch: the active rule and label cross over rather than snapping, so the strip
   confirms which section you are in while you scroll past it. */
.motion-ok .category-strip a { transition: border-bottom-color var(--kfc-dur-title-line) var(--kfc-ease-out); }
.motion-ok .category-strip span { transition: color var(--kfc-dur-title-line) var(--kfc-ease-out); }
.category-strip img { width: 56px; height: 56px; object-fit: contain; }
/* Overline style (Sans Regular 12 caps). Active = Digital Red + underline (KFC PLP tabs). */
.category-strip span { font-size: 13px; font-weight: 400; text-transform: uppercase; line-height: 16px; min-height: 32px; }
.category-strip a[aria-current="true"] { border-bottom-color: var(--kfc-digital-red); }
.category-strip a[aria-current="true"] span, .category-strip a:hover span { color: var(--kfc-digital-red); }
@media (min-width: 600px) {
    .category-strip a { width: 128px; }
    .category-strip img { width: 72px; height: 72px; }
    .category-strip span { font-size: 14px; line-height: 18px; min-height: 36px; }
}

/* m2: the offset is the MEASURED sticky strip height, published as a token, plus a gap.
   It was 96/120 against a strip of 122/138, so a #category-N deep link landed under it. */
.menu-section { padding: var(--kfc-space-8) 0 var(--kfc-space-2); scroll-margin-top: calc(var(--kfc-strip-h) + var(--kfc-space-2)); }
@media (min-width: 600px) { .menu-section { padding-top: var(--kfc-space-14); } }
.menu-section .category-title { margin-bottom: var(--kfc-space-5); }

/* M12: the home page's closing block. Where the restaurants are, whether they are open, and a
   number to call — not a decorative photo pair under a slogan. Plain rows on a rule, no cards. */
.restaurants { padding: var(--kfc-space-12) 0 var(--kfc-space-8); }
.restaurants .answer { margin-top: var(--kfc-space-3); }
.restaurant-list { list-style: none; margin: var(--kfc-space-6) 0 var(--kfc-space-5); padding: 0;
    display: grid; gap: var(--kfc-space-6); border-top: var(--kfc-border-400) solid var(--kfc-black); padding-top: var(--kfc-space-5); }
@media (min-width: 600px) { .restaurant-list { grid-template-columns: 1fr 1fr; column-gap: var(--kfc-grid-gutter); } }
@media (min-width: 1136px) { .restaurant-list { grid-template-columns: repeat(4, 1fr); } }
.restaurant-list li > * { margin: 0 0 var(--kfc-space-1); }
.restaurant-list h3 a { text-decoration: none; }
.restaurant-list h3 a:hover { color: var(--kfc-brand-red); }
.restaurant-address { font-size: 14px; line-height: 20px; color: var(--kfc-text-secondary); }

/* An answer block: one self-contained sentence, sized to be read first. It is what a model
   quotes, so it never depends on a heading or a table cell next to it for its meaning. */
.answer { font-size: 18px; line-height: 28px; max-width: 62ch; }
@media (min-width: 1136px) { .answer { font-size: 20px; line-height: 30px; } }

/* ------------------------------------------------------------------ informational pages */
/* P1-5. Four desktop columns gave each restaurant ~300px, which wrapped KFC ORANJESTAD onto two
   lines, broke a three-clause opening-hours sentence over three, and left the addresses, the
   phone numbers and the statuses at four different heights. Two columns at every width above a
   phone, with a real card boundary so it is obvious which facts belong to which restaurant. */
.location-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--w-gap-block);
    border-top: var(--kfc-border-400) solid var(--kfc-black); padding-top: var(--w-gap-block); }
@media (min-width: 600px) { .location-list { grid-template-columns: 1fr 1fr; gap: var(--w-gap-block) var(--kfc-grid-gutter); } }
/* The CTA is pushed to the bottom so they line up however many lines the addresses take. */
.location-list li { display: flex; flex-direction: column;
    border: var(--kfc-border-100) solid var(--kfc-grey-200); border-radius: var(--kfc-radius-none);
    padding: var(--w-gap-inline); }
@media (min-width: 600px) { .location-list li { padding: var(--w-gap-block); } }
.location-list li > * { margin: 0 0 var(--w-gap-tight); }
/* Below 600px the card is the full width of a phone and the name still has to fit on one line,
   so the status drops beneath it rather than taking 85px off the side of the heading — the same
   rule the home page's restaurant row follows. */
.location-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 2px var(--kfc-space-3); }
.location-head .status { flex-basis: 100%; }
@media (min-width: 1136px) { .location-head .status { flex-basis: auto; } }
.location-head h2 { margin: 0; overflow-wrap: anywhere; }
.location-list h2 a { text-decoration: none; }
.location-list h2 a:hover { color: var(--kfc-brand-red); }
.location-address, .location-hours { font-size: 14px; line-height: 20px; color: var(--kfc-text-secondary); }
/* Specific enough to beat `.location-list li > *`, which resets every margin in the column. */
.location-list li > .location-order { margin-top: auto; padding-top: var(--w-gap-inline); margin-bottom: 0; }

.fact-list { margin: var(--kfc-space-6) 0 var(--kfc-space-8); border-top: var(--kfc-border-400) solid var(--kfc-black); }
.fact-list > div { display: grid; grid-template-columns: 1fr; gap: 2px; padding: var(--kfc-space-3) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
@media (min-width: 600px) { .fact-list > div { grid-template-columns: 180px minmax(0, 1fr); gap: var(--kfc-space-4); } }
.fact-list dt { font-weight: 400; font-size: 14px; color: var(--kfc-text-secondary); }
.fact-list dd { margin: 0; }

.qa section { padding: var(--kfc-space-6) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
.qa section:first-child { border-top: var(--kfc-border-400) solid var(--kfc-black); }
.qa h2 { margin-bottom: var(--kfc-space-3); }
.qa .answer { margin: 0; }
.updated { margin-top: var(--kfc-space-8); }

/* ------------------------------------------------------------------ footer (p5)
   Two columns of real information: the NAP for all four restaurants, and where everything is. */
.site-footer { border-top: var(--kfc-border-400) solid var(--kfc-black); padding: var(--kfc-space-8) 0 var(--kfc-space-10); font-size: 14px; line-height: 20px; color: var(--kfc-text); }
.footer-grid { display: grid; gap: var(--kfc-space-8); }
@media (min-width: 900px) { .footer-grid { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); column-gap: var(--kfc-space-12); } }
.site-footer h2 { font-size: 16px; line-height: 20px; margin: 0 0 var(--kfc-space-4); }
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.footer-stores ul { display: grid; gap: var(--kfc-space-4); }
@media (min-width: 600px) { .footer-stores ul { grid-template-columns: 1fr 1fr; column-gap: var(--kfc-grid-gutter); } }
.footer-stores li { display: flex; flex-direction: column; gap: 2px; }
.footer-stores span { color: var(--kfc-text-secondary); }
.footer-stores a { text-underline-offset: 3px; }
.footer-hours { margin: var(--kfc-space-4) 0 0; color: var(--kfc-text-secondary); }
.footer-links li { margin-bottom: var(--kfc-space-2); }
.footer-delivery { margin: var(--kfc-space-4) 0 0; color: var(--kfc-text-secondary); }
.footer-legal { margin: var(--kfc-space-8) 0 0; padding-top: var(--kfc-space-4); border-top: var(--kfc-border-100) solid var(--kfc-grey-200); font-size: 12px; line-height: 16px; color: var(--kfc-text-secondary); }

/* Payment method chooser: plain radios, one per row, in the same square-cornered style as the
   rest of the checkout. No pills, no icons, no cards. */
.pay-methods { display: grid; gap: var(--kfc-space-2); margin-bottom: var(--kfc-space-6); }
.pay-method { display: flex; align-items: flex-start; gap: var(--kfc-space-3); min-height: var(--tap);
    padding: var(--kfc-space-3); border: var(--kfc-border-100) solid var(--kfc-control-line);
    background: var(--kfc-white); cursor: pointer; }
/* The one chosen treatment (see the design system): red line, light red fill. The native radio
   is the mark, so this control needs no drawn one. */
.pay-method:has(input:checked) { border-color: var(--w-chosen-line); border-width: var(--w-chosen-width); background: var(--w-chosen-fill); }
.pay-method input { margin-top: 2px; accent-color: var(--kfc-digital-red); width: 18px; height: 18px; }
.pay-method > span { display: flex; flex-direction: column; gap: 2px; }

/* "Delivery by VapVap Eats": the rider brand, white-on-black only (the logo has no dark variant). */
.delivered-by { display: inline-flex; align-items: center; gap: 8px; background: var(--kfc-black);
    color: var(--kfc-white); padding: 6px 10px; font-size: 12px; line-height: 1; }
.delivered-by img { display: block; height: 18px; width: auto; }

/* Category header band. B6: rendered ONLY for artwork that is a genuine wide banner
   (OfficialPhotos::isBanner — measured, not declared); a square packshot gets no band at all,
   because it is already the strip tile 120px above and the band was a screenful of white around
   a duplicate. What is left is a real page banner (spec §6): full-bleed to the page edges, 16:9
   capped in height. No text sits on it, so there is no scrim and no gradient — the title stays
   below, in Brand Red. The cover crop here is correct and is NOT the packshot bug: a banner is
   shot to be cropped, a packshot is not, and a packshot never reaches this rule. */
.category-band { margin: 0 calc(-1 * var(--kfc-grid-margin)) var(--kfc-space-5); background: var(--kfc-grey-50); }
.category-band img { display: block; width: 100%; aspect-ratio: 3 / 2; max-height: 300px; object-fit: cover; object-position: center 45%; }
@media (min-width: 600px) { .category-band img { aspect-ratio: 16 / 9; max-height: 360px; } }
.category-all { margin: calc(-1 * var(--kfc-space-3)) 0 var(--kfc-space-2); }
.menu-section .category-title { margin-bottom: var(--kfc-space-2); }

/* Subcategory heading inside a category (Deliverect's full category hierarchy). */
/* Weight 400, not 700: Kentucky Fried Sans ships Light and Regular only and the body sets
   `font-synthesis-weight: none`, so 700 here rendered as Regular and the heading was carrying a
   weight it never had. The step up from the body is the size, the caps and the tracking. */
.subcategory-title { font-family: var(--kfc-font-sans); font-weight: 400; font-size: 18px; line-height: 1.2;
    text-transform: uppercase; letter-spacing: .01em; color: var(--kfc-grey-800);
    margin: var(--kfc-space-8) 0 var(--kfc-space-4); }
@media (min-width: 600px) { .subcategory-title { font-size: 22px; } }
.menu-section .product-grid + .subcategory-title { margin-top: var(--kfc-space-10); }

/* ------------------------------------------------------------------ product cards (radius 0; 8/8/4/12 stack, stated) */
.product-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--w-gap-block) var(--kfc-grid-gutter); }
@media (min-width: 600px) { .product-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--w-gap-section) var(--kfc-grid-gutter); } }
@media (min-width: 1136px) { .product-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.product { display: flex; flex-direction: column; height: 100%; border-radius: var(--kfc-radius-none); }
/* One image container and one ratio for every product on the site (--w-card-media), and the
   photograph is CENTRED in it. It used to be left-aligned, which is defensible for a card in a
   grid on its own but is not what the kiosk does for the same packshot: a tall item hugged the
   left edge and a wide one filled the cell, so two cards in a row carried their subject in two
   different places. */
.product-photo { aspect-ratio: var(--w-card-media); background: var(--kfc-white); display: flex; align-items: center; justify-content: center; margin-bottom: var(--w-gap-tight); }
.product-photo img { width: 100%; height: 100%; object-fit: contain; object-position: center; }
.product.is-sold-out .product-photo img { opacity: .45; filter: grayscale(1); }

/* Product tag (observed KFC pattern): Sans caps in Digital Red, 1px Digital Red outline, radius 4.
   Digital Red on white = 4.59:1, AA at 12px. min-height reserves the row on every card. */
.tags { display: flex; flex-wrap: wrap; gap: var(--kfc-space-1); margin-bottom: var(--kfc-space-2); min-height: 22px; }
.tag {
    display: inline-flex; align-items: center; border: var(--kfc-border-100) solid var(--kfc-digital-red); border-radius: var(--kfc-radius-40);
    color: var(--kfc-digital-red); font-size: 12px; font-weight: 400; text-transform: uppercase; line-height: 16px; padding: 2px var(--kfc-space-1);
}
.tag-sold-out { border-color: var(--kfc-grey-500); color: var(--kfc-grey-500); }

/* M1 + M2: product names come from the POS and are supplier-controlled — a 96-character name
   rendered 7 lines, stretched its grid row and left ~180px of dead white inside the neighbouring
   card. Clamped to two lines (three on desktop) with that height RESERVED, so price and
   description line up across a row whatever the names do. The full name is on the product page. */
/* The name is what the customer READS, so it is the sans in Title Case — the same face and the
   same casing the kiosk shows for the same product (see the type roles in the design system).
   It overrides the blanket h1-h4 heading rule, which is what used to set it in display italic
   caps. Caps are also ~12% wider, so Title Case fits more of a POS name in the two reserved
   lines. Clamped to 2 lines with the height RESERVED at every width, so the price, the
   description and the action line up across a row whatever the names do. */
.product-name {
    font-family: var(--kfc-font-sans); font-style: normal; font-weight: 400; text-transform: none;
    font-size: var(--w-type-item); line-height: 18px; letter-spacing: 0; margin-bottom: var(--w-gap-tight); overflow-wrap: anywhere;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    min-height: var(--w-card-name);
}
.product-price { font-weight: 400; font-size: var(--w-type-money); line-height: 20px; margin: 0 0 var(--kfc-space-1); }
/* Reserved as well: with only the name reserved, a one-line description still moved the price
   of the card beside it by a line. */
.product-desc {
    font-size: 14px; line-height: 20px; color: var(--kfc-text-secondary); margin: 0 0 var(--w-gap-tight);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    min-height: var(--w-card-desc);
}
.product .btn { margin-top: auto; padding: 0 var(--kfc-space-2); }
@media (min-width: 600px) { .product-name { line-height: 20px; } }
@media (min-width: 1136px) {
    .product-name { line-height: 25px; }
    .product-desc { -webkit-line-clamp: 1; }
}

/* ------------------------------------------------------------------ cart bar */
/* The review rates this bar as the clearest thing on the mobile site and asks for it to be kept.
   What it asked for around it is room: the bar carried 12px above and below a 44px button, which
   is a 68px black band with its contents touching the edges. --w-gap-inline on both. */
.cart-bar {
    position: sticky; bottom: 0; z-index: 25; background: var(--kfc-black); color: var(--kfc-white);
    padding: var(--w-gap-inline) var(--kfc-grid-margin) calc(var(--w-gap-inline) + env(safe-area-inset-bottom));
}
.cart-bar .wrap { padding: 0; display: flex; align-items: center; gap: var(--w-gap-inline); }
.cart-bar p { margin: 0; flex: 1; font-size: 16px; font-weight: 400; }
.cart-bar .btn { min-height: var(--w-tap-inline); }
.cart-bar[hidden] { display: none; }

/* ------------------------------------------------------------------ product sheet
   Dialog: radius 16 (stated); on phones docked to the bottom with top corners only. Open/close
   280ms (a sheet is lighter than KFC's measured 450ms full-page push). */
dialog.sheet {
    padding: 0; border: 0; margin: 0; width: 100%; max-width: 100%; max-height: 92vh; max-height: 92dvh;
    top: auto; bottom: 0; left: 0; right: 0; border-radius: var(--kfc-radius-200) var(--kfc-radius-200) 0 0;
    background: var(--kfc-white); color: var(--kfc-black); overflow: hidden;
}
dialog.sheet[open] { display: flex; flex-direction: column; }
dialog.sheet::backdrop { background: rgba(0, 0, 0, .6); }
@media (min-width: 600px) {
    dialog.sheet { top: 50%; bottom: auto; left: 50%; right: auto; transform: translate(-50%, -50%); width: 560px; max-height: 88vh; border-radius: var(--kfc-radius-200); }
}
.sheet-form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; margin: 0; }
.sheet-body { overflow-y: auto; overflow-x: hidden; padding: 0 var(--kfc-space-4) var(--kfc-space-4); flex: 1 1 auto; min-height: 0; overscroll-behavior: contain; }
@media (min-width: 600px) { .sheet-body { padding: 0 var(--kfc-space-6) var(--kfc-space-4); } }

/* M11: a real bar above the scroll container, not a button floating over it. The close button
   used to be position: absolute, so the item name scrolled underneath it and was sliced in half
   by the dialog's overflow: hidden — the sheet's normal state, because a long option list means
   the customer is always scrolled. The bar carries its own copy of the name, which crosses in
   only once the big title has scrolled away, so the name is never on screen twice. */
.sheet-head { display: flex; align-items: center; gap: var(--kfc-space-2); flex: none;
    padding: var(--kfc-space-2) var(--kfc-space-2) var(--kfc-space-2) var(--kfc-space-4); background: var(--kfc-white); }
@media (min-width: 600px) { .sheet-head { padding-left: var(--kfc-space-6); } }
.sheet-head-title {
    flex: 1; min-width: 0; margin: 0; opacity: 0;
    font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase;
    font-size: 16px; line-height: 20px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: opacity var(--kfc-dur-title-line) var(--kfc-ease-out);
}
dialog.sheet.is-scrolled .sheet-head { border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
dialog.sheet.is-scrolled .sheet-head-title { opacity: 1; }
.sheet-close {
    flex: none; width: var(--tap); height: var(--tap);
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-radius: var(--kfc-radius-40); background: var(--kfc-white); color: var(--kfc-black); cursor: pointer; padding: 0;
}
/* p3: the photo sat left with an empty right half. Left-aligned is right for a CARD in a grid;
   a dialog is one object on its own, so it is centred and allowed the width. */
.sheet-photo { aspect-ratio: 4 / 3; max-height: 260px; width: 100%; display: flex; align-items: center; justify-content: center; margin: var(--kfc-space-2) 0; }
.sheet-photo img { max-height: 100%; max-width: 100%; width: auto; object-fit: contain; }
.sheet-title { font-size: 28px; line-height: 28px; margin: var(--kfc-space-2) 0; }
.sheet-price { font-weight: 400; margin: 0 0 var(--kfc-space-1); }
.sheet-desc { color: var(--kfc-text-secondary); font-size: 14px; line-height: 24px; }
.sheet-loading { padding: var(--kfc-space-16) 0; text-align: center; color: var(--kfc-text-secondary); }

.group { border: 0; padding: 0; margin: var(--kfc-space-5) 0 0; min-width: 0; }
/* m4: flex-wrap + margin-left:auto dropped REQUIRED onto its own line, right-aligned under
   "Choose 1", on every group whose name is long — which looks accidental. Three columns: the
   name takes what is left and wraps inside its own cell, and the rule and the badge stay put. */
.group legend { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; align-items: baseline; column-gap: var(--kfc-space-3); width: 100%; padding: 0 0 var(--kfc-space-2); border-bottom: var(--kfc-border-300) solid var(--kfc-black); }
.group-name { font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase; font-size: 20px; line-height: 20px; overflow-wrap: anywhere; }
.group-rule { font-size: 14px; color: var(--kfc-text-secondary); white-space: nowrap; }
.group-required { font-size: 12px; line-height: 16px; font-weight: 400; text-transform: uppercase; color: var(--kfc-black); white-space: nowrap; }
.group-required.is-met { color: var(--kfc-text-secondary); }
.group.has-error legend { border-bottom-color: var(--kfc-error-main); }
.group.has-error .group-required { color: var(--kfc-error-dark); }
.group-error { margin: var(--kfc-space-2) 0 0; }
.options { list-style: none; margin: 0; padding: 0; }
.option { border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
.option label, .option .option-row { display: flex; align-items: center; gap: var(--kfc-space-3); min-height: 52px; padding: var(--kfc-space-1) var(--w-gap-tight); cursor: pointer; }
/* The chosen treatment on a LIST ROW rather than a card: same fill, same red line, but the line
   is the row's leading edge instead of a box, because these rows sit in an edge-to-edge stack
   inside a 560px sheet and a full outline around one of them reads as an error box. The mark is
   the native radio or checkbox, in Brand Red. */
.option:has(input:checked) > label, .option:has(> .option-row input:checked) > .option-row {
    background: var(--w-chosen-fill); box-shadow: inset var(--w-chosen-width) 0 0 var(--w-chosen-line);
}
.option input[type="checkbox"], .option input[type="radio"] { width: 22px; height: 22px; margin: 0; accent-color: var(--kfc-brand-red); flex: none; }
.option .option-name { flex: 1; }
.option .option-price { font-size: 14px; color: var(--kfc-text-secondary); white-space: nowrap; }
.option.is-sold-out { color: var(--kfc-text-secondary); }
.option.is-sold-out label { cursor: not-allowed; }
.option .nested { padding-left: 34px; padding-bottom: var(--kfc-space-2); }

.stepper { display: inline-flex; align-items: center; border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40); }
.stepper button {
    width: var(--w-tap-inline); height: var(--w-tap-inline); border: 0; background: none; cursor: pointer; padding: 0; color: var(--kfc-black);
    display: inline-flex; align-items: center; justify-content: center; border-radius: var(--kfc-radius-40);
}
.stepper button[disabled] { color: var(--kfc-grey-300); cursor: not-allowed; }
.stepper output, .stepper .qty { min-width: 28px; text-align: center; font-weight: 400; }

.sheet-foot { border-top: var(--kfc-border-100) solid var(--kfc-grey-200); padding: var(--kfc-space-3) var(--kfc-space-4) calc(var(--kfc-space-3) + env(safe-area-inset-bottom)); background: var(--kfc-white); }
.sheet-foot .row { display: flex; gap: var(--kfc-space-3); align-items: center; }
.sheet-foot .btn { flex: 1; padding: 0 var(--kfc-space-3); white-space: nowrap; }
.sheet-error { margin: 0 0 10px; }

/* ------------------------------------------------------------------ forms (inputs radius 4) */
.field { margin: 0 0 var(--kfc-space-4); }
.field label, .label { display: block; font-weight: 400; font-size: 14px; line-height: 20px; margin-bottom: 6px; }
.field .hint { font-size: 12px; line-height: 16px; color: var(--kfc-text-secondary); margin: var(--kfc-space-1) 0 0; }
.input, .field input, .field select, .field textarea {
    width: 100%; min-height: var(--tap); padding: 10px var(--kfc-space-3);
    border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40);
    background: var(--kfc-white); font-size: 16px; font-weight: 400;
}
.field textarea { min-height: 88px; resize: vertical; }
.input:hover, .field input:hover, .field select:hover, .field textarea:hover { border-color: var(--kfc-black); }
.input:focus, .field input:focus, .field select:focus, .field textarea:focus { border-color: var(--kfc-black); outline: var(--kfc-focus-width) solid var(--kfc-focus); outline-offset: 2px; }
/* Invalid is a red BOUNDARY, not red typing: the customer's own name came back at them in error
   red, which reads as though the characters were wrong rather than the field being empty. */
.field input[aria-invalid="true"] { border-color: var(--kfc-error-main); }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--kfc-space-3); }
fieldset.form-step { border: 0; padding: 0; margin: 0 0 var(--kfc-space-6); min-width: 0; }
fieldset.form-step legend { padding: 0; margin-bottom: var(--kfc-space-4); }
.section-rule { border: 0; border-top: var(--kfc-border-100) solid var(--kfc-grey-200); margin: var(--kfc-space-6) 0; }

/* Narrow pages: the max-width is the CONTENT width, so add the grid margins back. */
.narrow { max-width: 560px; }
.wrap.narrow { max-width: calc(560px + 2 * var(--kfc-grid-margin)); }
.wrap.narrow.track { max-width: calc(640px + 2 * var(--kfc-grid-margin)); }  /* order status: long status on one line */

/* ------------------------------------------------------------------ cart / order summaries */
.two-col { display: grid; gap: var(--kfc-space-8); }
@media (min-width: 1136px) { .two-col { grid-template-columns: minmax(0, 1fr) 380px; align-items: start; gap: var(--kfc-space-12); } }

.lines { list-style: none; margin: 0; padding: 0; border-top: var(--kfc-border-400) solid var(--kfc-black); }
/* M5: a 1:1 menu photo is the first column (spec §6 puts menu photography in the cart and the
   checkout order summary). The photo spans every text row, so the line reads as one block. */
/* P1-6. The row was ~150px tall on a phone: three text rows, then a full-width actions row that
   started back under the photograph, so the quantity control belonged to the line visually only
   by being underneath it. The actions now sit in the text column, the padding is one step
   tighter and the unit line only renders when the quantity is more than one (which is the rule
   the kiosk cart already follows). Nothing was removed. */
.line { display: grid; grid-template-columns: var(--w-thumb) minmax(0, 1fr) auto; gap: var(--kfc-space-1) var(--kfc-space-3); padding: var(--kfc-space-3) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
.line-photo { grid-column: 1; grid-row: 1 / span 4; width: var(--w-thumb); height: var(--w-thumb); object-fit: contain; align-self: start; }
/* The same product, in the same face and the same casing, as the card it was added from and as
   the kiosk's own cart line (--w-type-item). */
.line-name { grid-column: 2; font-family: var(--kfc-font-sans); font-style: normal; font-weight: 400; text-transform: none; font-size: var(--w-type-item); line-height: 22px; }
.line-summary { grid-column: 2 / -1; font-size: 14px; line-height: 20px; color: var(--kfc-text-secondary); margin: 0; }
/* p2: at quantity 12 the line read "AWG 1.114,00" with no unit price anywhere on the page. */
.line-unit { grid-column: 2 / -1; font-size: 14px; line-height: 20px; color: var(--kfc-text-secondary); margin: 0; }
.line-unit[hidden] { display: none; }
.line-total { grid-column: 3; font-weight: 400; white-space: nowrap; }
.line-actions { grid-column: 2 / -1; display: flex; align-items: center; gap: var(--kfc-space-4); margin-top: var(--kfc-space-1); }
/* An unavailable line has no photo: its text takes the whole row back. */
.line.is-unavailable .line-name, .line.is-unavailable .line-summary { grid-column: 1 / -1; }
.line.is-unavailable .line-name { color: var(--kfc-text-secondary); }

.summary { border: var(--kfc-border-100) solid var(--kfc-grey-200); border-radius: var(--kfc-radius-100); padding: var(--kfc-space-5); }
.summary h2 { margin-bottom: var(--kfc-space-4); }
.summary dl { margin: 0 0 var(--kfc-space-4); }
.summary .row { display: flex; justify-content: space-between; gap: var(--kfc-space-3); padding: var(--kfc-space-1) 0; }
.summary dt, .summary dd { margin: 0; }
.summary .total { border-top: var(--kfc-border-300) solid var(--kfc-black); margin-top: var(--kfc-space-2); padding-top: var(--kfc-space-3); font-family: var(--kfc-font-serif); font-style: italic; font-weight: 900; text-transform: uppercase; font-size: 22px; line-height: 24px; }
.summary .tax-note { font-size: 14px; color: var(--kfc-text-secondary); }
.summary-lines { list-style: none; padding: 0; margin: 0 0 var(--kfc-space-3); font-size: 14px; line-height: 20px; }
.summary-lines li { display: flex; align-items: center; justify-content: space-between; gap: var(--kfc-space-3); padding: var(--kfc-space-2) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }
.summary-lines li > span { flex: 1; min-width: 0; }
.summary-lines .line-photo { flex: none; width: 56px; height: 56px; object-fit: contain; }
.summary-lines strong { white-space: nowrap; }
.summary-lines .sub { display: block; font-size: 12px; line-height: 16px; color: var(--kfc-text-secondary); }

/* M7: the checkout order summary is the SECOND column of .two-col, so below 1136px it stacked
   after the whole form and after the CTA — a customer could place an order without ever having
   seen what they were buying. Below the breakpoint it goes first and collapses to a count and a
   total; at 1136px it is a column again and checkout.js reopens it. */
/* P0-2. One compact row: "Your order" over "4 items" on the left, the figure and the chevron on
   the right, each of them on ONE line. The old row put the heading and a single
   "4 items · AWG 69,80" string in competition for the same 318px and both of them wrapped —
   which made the one control on the page that tells a customer what they are about to pay for
   look like a rendering accident. */
details.summary > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: var(--w-gap-inline); }
details.summary > summary::-webkit-details-marker { display: none; }
.summary-label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
details.summary > summary h2 { margin: 0; font-size: 20px; line-height: 24px; }
.summary-count { font-size: 14px; line-height: 18px; font-weight: 400; color: var(--kfc-text-secondary); }
.summary-amount { margin-left: auto; font-size: 18px; line-height: 24px; font-weight: 400; white-space: nowrap; }
.summary-toggle { flex: none; display: inline-flex; align-items: center; color: var(--kfc-black); }
.motion-ok .summary-toggle { transition: transform var(--kfc-dur-press) var(--kfc-ease-out); }
details.summary[open] .summary-toggle { transform: rotate(180deg); }
details.summary[open] > summary { margin-bottom: var(--kfc-space-4); }
/* Open, the count and the figure are the list and the Total row directly underneath, so the row
   keeps only what is still doing work: the heading and the way to shut it again. */
details.summary[open] .summary-count, details.summary[open] .summary-amount { display: none; }
@media (max-width: 1135px) { #order-summary { order: -1; } }
@media (min-width: 1136px) {
    details.summary > summary { cursor: default; }
    details.summary > summary h2 { font-size: 32px; line-height: 32px; }
    /* Above the breakpoint the summary is a column that is always open and never toggled. */
    .summary-count, .summary-amount, .summary-toggle { display: none; }
}

/* M6: the cart's own sticky checkout bar. Same component as the menu's cart bar; hidden at the
   width where the summary column is already beside the lines. */
.cart-bar-checkout { margin: var(--kfc-space-6) calc(-1 * var(--kfc-grid-margin)) 0; }
.cart-bar-checkout .btn { min-height: 44px; }
@media (min-width: 1136px) { .cart-bar-checkout { display: none; } }

.empty { padding: var(--kfc-space-6) 0; }
.empty p { font-size: 18px; }

/* ------------------------------------------------------------------ card fields (Microform iframes) */
.card-box { height: var(--tap); border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40); padding: var(--kfc-space-1) var(--kfc-space-3); background: var(--kfc-white); }
.card-box.cvv { max-width: 140px; }

/* Payment in progress: KFC brand loader + status text (checkout.js toggles [hidden]). */
.processing { margin: var(--kfc-space-4) 0 0; padding: var(--kfc-space-3) 0; text-align: center; }
.processing[hidden] { display: none; }
.processing p { margin: var(--kfc-space-2) 0 0; font-weight: 400; }
/* Brand loader on its own row, centred. 220px wide = the mark renders ~84px (it fills ~38% of the 4:5 frame). */
.status-motion, .kfc-motion.status-motion { display: block; width: 220px; max-width: 60%; margin: 0 auto; }
.status-motion[hidden], .kfc-motion.status-motion[hidden] { display: none; }
.status-motion .kfc-motion { display: block; width: 100%; }

/* 3DS challenge overlay (checkout.js toggles style.display) */
.challenge { position: fixed; inset: 0; z-index: 60; background: rgba(0, 0, 0, .6); align-items: center; justify-content: center; padding: var(--kfc-space-3); }
.challenge-box { background: var(--kfc-white); padding: var(--kfc-space-4); max-width: 100%; border-radius: var(--kfc-radius-200); }
.challenge-box iframe { width: 400px; max-width: 100%; height: 600px; max-height: 75vh; border: 0; display: block; margin: var(--kfc-space-3) 0; }

/* ------------------------------------------------------------------ order tracking */
.track-title { color: var(--kfc-brand-red); }
.status-block { margin: var(--kfc-space-5) 0 var(--kfc-space-4); text-align: center; }
.status-big { margin: 0 0 var(--kfc-space-2); }
.progress { list-style: none; margin: 0 0 var(--kfc-space-2); padding: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
/* An order that never reached the kitchen has no progress to show, and `display: grid` above
   would otherwise beat the browser's own [hidden] rule and draw four empty bars. */
.progress[hidden] { display: none; }
/* The step the order is stuck ON when the restaurant has not confirmed it: amber, not red (the
   order is fine, it is late), and not filled, because that step has not actually happened. */
.progress li.is-stalled::before { background: var(--kfc-warning-main); }
.progress li.is-stalled { color: var(--kfc-grey-800); font-weight: 400; }
.progress li { font-size: 12px; line-height: 16px; color: var(--kfc-text-secondary); }
.progress li::before { content: ""; display: block; height: 8px; background: var(--kfc-grey-200); margin-bottom: 6px; transition: background-color var(--kfc-dur-title-line) var(--kfc-ease-out); }
.progress li.is-done { color: var(--kfc-black); font-weight: 400; }
.progress li.is-done::before { background: var(--kfc-brand-red); }
.order-meta { margin: var(--kfc-space-5) 0; font-size: 14px; line-height: 20px; }
.payment-note { display: flex; align-items: center; gap: var(--kfc-space-3); }

.orders-list { list-style: none; margin: 0; padding: 0; border-top: var(--kfc-border-400) solid var(--kfc-black); }
.orders-list a { display: grid; grid-template-columns: 1fr auto; gap: 2px var(--kfc-space-3); padding: 14px 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); text-decoration: none; min-height: var(--tap); }
.orders-list a:hover .order-number { color: var(--kfc-brand-red); }
/* A row title in a list, like a cart line: the text face (--w-type-item), not the display one. */
.order-number { font-family: var(--kfc-font-sans); font-style: normal; font-weight: 400; font-size: var(--w-type-item); line-height: 22px; }
.orders-list .when { grid-column: 1; font-size: 14px; color: var(--kfc-text-secondary); }
.orders-list .amount { font-weight: 400; text-align: right; }
.orders-list .state { grid-column: 2; font-size: 14px; text-align: right; }

.address-list { list-style: none; margin: 0 0 var(--kfc-space-4); padding: 0; }
.address-list li { display: flex; align-items: center; justify-content: space-between; gap: var(--kfc-space-3); padding: var(--kfc-space-2) 0; border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); }

.pagination-wrap { margin-top: var(--kfc-space-5); }
.field input.code-input { font-size: 28px; letter-spacing: 0.3em; font-weight: 400; max-width: 240px; }
/* (the border width for an invalid field is set once, with the field errors below.) */

/* ------------------------------------------------------------------ brand motion (<x-kfc-motion>)
   The still (poster <img>) sits BEHIND the clip and is hidden only while the clip is actually
   playing. The clips have alpha, so a still under a playing frame would ghost through it — but a
   clip that never decodes, or whose autoplay is refused, has to leave the KFC mark on screen
   rather than a hole in the middle of a payment screen. motion.js adds .is-playing on `playing`
   and takes it off again on `error` or a refused play(). */
.kfc-motion { display: inline-block; position: relative; line-height: 0; }
.kfc-motion video, .kfc-motion img { width: 100%; height: auto; display: block; }
.kfc-motion-still { position: absolute; inset: 0 0 auto 0; width: 100%; height: auto; }
.kfc-motion.is-playing .kfc-motion-still { visibility: hidden; }
.kfc-motion.is-done { visibility: hidden; }

/* ------------------------------------------------------------------ UI motion (MEASURED timings, stated easing)
   Everything below only runs with html.motion-ok (set by motion.js when reduced motion is off), so
   content never stays hidden if JS fails. */
@keyframes kfc-line-in { from { opacity: 0; transform: translateY(var(--kfc-line-offset, .5em)); } to { opacity: 1; transform: none; } }
.motion-ok [data-reveal]:not(.is-in) { opacity: 0; }
/* Anything already on screen at first paint is never revealed: motion.js marks it instant, so a
   scroll-reveal can never be the reason the largest element on the page paints late. */
.motion-ok [data-reveal].is-instant { animation: none !important; opacity: 1; }
.motion-ok [data-reveal="title"].is-in { --kfc-line-offset: .4em; animation: kfc-line-in var(--kfc-dur-title-line) var(--kfc-ease-out) both; animation-delay: calc(var(--i, 0) * var(--kfc-stagger-title)); }
.motion-ok [data-reveal="body"].is-in { --kfc-line-offset: .5em; animation: kfc-line-in 300ms var(--kfc-ease-out) both; animation-delay: calc(var(--i, 0) * var(--kfc-stagger-body)); }

/* Sheet open/close 280ms: slide up on phones, rise + fade on desktop. Close = .is-closing. */
@keyframes kfc-sheet-up { from { transform: translateY(100%); } to { transform: none; } }
@keyframes kfc-sheet-down { from { transform: none; } to { transform: translateY(100%); } }
@keyframes kfc-modal-in { from { opacity: 0; transform: translate(-50%, calc(-50% + 40px)); } to { opacity: 1; transform: translate(-50%, -50%); } }
@keyframes kfc-modal-out { from { opacity: 1; transform: translate(-50%, -50%); } to { opacity: 0; transform: translate(-50%, calc(-50% + 40px)); } }
@keyframes kfc-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes kfc-fade-out { from { opacity: 1; } to { opacity: 0; } }
.motion-ok dialog.sheet[open] { animation: kfc-sheet-up var(--kfc-dur-sheet) var(--kfc-ease-out); }
.motion-ok dialog.sheet[open].is-closing { animation: kfc-sheet-down var(--kfc-dur-sheet) var(--kfc-ease-standard) forwards; }
.motion-ok dialog.sheet[open]::backdrop { animation: kfc-fade-in var(--kfc-dur-sheet) var(--kfc-ease-out); }
.motion-ok dialog.sheet[open].is-closing::backdrop { animation: kfc-fade-out var(--kfc-dur-sheet) var(--kfc-ease-standard) forwards; }
@media (min-width: 600px) {
    .motion-ok dialog.sheet[open] { animation-name: kfc-modal-in; }
    .motion-ok dialog.sheet[open].is-closing { animation-name: kfc-modal-out; }
}

/* Skeleton -> content: the sheet shows "Loading" until the product arrives. Fading the real
   content in tells the customer the wait ended; it is 180ms, so it never feels like more waiting. */
@keyframes kfc-content-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.motion-ok .sheet-body.is-ready > * { animation: kfc-content-in 180ms var(--kfc-ease-out) both; }

/* Page transitions between storefront routes. Cross-document, so back/forward keep working, the
   URLs stay real and a browser without View Transitions simply navigates. Reduced motion turns
   the animations off above, which leaves an instant swap. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: kfc-fade-out 140ms var(--kfc-ease-in) both; }
::view-transition-new(root) { animation: kfc-fade-in 220ms var(--kfc-ease-out) both; }

/* Add-to-cart success: subtle emphasis scale on the product button, count bump in the header. */
@keyframes kfc-emphasis { 0% { transform: scale(1); } 40% { transform: scale(1.045); } 100% { transform: scale(1); } }
@keyframes kfc-bump { 0% { transform: translateY(0); } 35% { transform: translateY(-40%); opacity: 0; } 36% { transform: translateY(40%); } 100% { transform: none; opacity: 1; } }
.motion-ok .is-emphasis { animation: kfc-emphasis 450ms var(--kfc-ease-out); }
.motion-ok .is-bump { display: inline-block; animation: kfc-bump 300ms var(--kfc-ease-standard); }

/* Reduced motion means NOT animating, not animating faster: `animation: none` rather than a 1ms
   duration. Nothing here depends on an animationend firing — menu.js closes the sheet instantly
   when reduced motion is on, and motion.js's replay() returns early — and no [data-reveal] can be
   left invisible, because html.motion-ok is absent, which is what hides them in the first place. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
        scroll-behavior: auto !important;
    }
    ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
    .btn:active { transform: none; }
    .btn-roll > span + span { display: none; }
    .btn-roll > span:first-child { transform: none !important; }
    video[data-kfc-motion] { display: none; }
    /* Nothing plays, so the still is the whole clip and takes the wrapper's height back. */
    .kfc-motion-still { position: static; visibility: visible; }
}

/* "Nearest to you: X · Switch" (store-locate.js, when the customer is already using the page). */
.nearest-line .nearest-switch { display: inline-flex; align-items: center; min-height: 24px; padding: 0 2px; font-size: 14px; }

/* ------------------------------------------------------------------ error pages (resources/views/errors/)
   Product surface, not a stack trace: KFC header and footer, one heading, one line, one way out. */
/* P2-9. The copy is right; it was floating. The block is centred on its own axis rather than
   left-ranged inside a centred column, and the 64px of padding under it — on top of the page's
   own 56 — left roughly 140px of nothing between "Find a KFC restaurant" and the footer rule.
   The page frame owns the bottom space now, so there is one gap and not two. */
.error-page { padding: var(--w-gap-section) 0 0; max-width: 560px; margin: 0 auto; text-align: center; }
.error-page h1 { color: var(--kfc-brand-red); }
.error-lead { margin: var(--w-gap-block) 0 var(--w-gap-section); font-size: 18px; line-height: 28px; }
.error-second { margin: var(--w-gap-block) 0 0; }

/* ------------------------------------------------------------------ breadcrumbs
   Plain text trail, Sans Regular caps at caption size. No chevron icons: the separator is
   typographic so it never reads as a functional icon (spec §5). */
.breadcrumbs { margin: 0 0 var(--kfc-space-3); }
@media (min-width: 600px) { .breadcrumbs { margin-bottom: var(--kfc-space-5); } }
.breadcrumbs ol { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--kfc-space-1) 0; font-size: 12px; line-height: 16px; }
.breadcrumbs li + li::before { content: "/"; margin: 0 var(--kfc-space-2); color: var(--kfc-grey-300); }
.breadcrumbs a { color: var(--kfc-text-secondary); text-decoration: none; text-underline-offset: 3px; }
.breadcrumbs a:hover { color: var(--kfc-black); text-decoration: underline; }
.breadcrumbs [aria-current="page"] { color: var(--kfc-black); }

/* ------------------------------------------------------------------ product page (/menu/{category}/{product})
   The indexable surface for one item, and the ordering path with JavaScript switched off: the
   modifier groups below are a real <form> that posts to /cart/lines. */
/* P0. On a 390x844 phone the photograph was 292px of a 4:3 box, the heading took two lines of
   32px, the description two more and the restaurant status a further two rows with a 48px tap
   target under it — so the first thing the page actually ASKS ("Original Recipe or Hot &
   Spicy?") began at y=723 and its options were off the bottom of the glass. The photograph is a
   header here, not a poster: the kiosk cut its sheet's image from 320 to 200 for the same
   reason and this is that decision at phone scale. Nothing is removed, only sized. */
.product-page { display: grid; gap: var(--w-gap-inline); }
@media (min-width: 600px) { .product-page { gap: var(--w-gap-block); } }
@media (min-width: 1136px) { .product-page { grid-template-columns: minmax(0, 520px) minmax(0, 1fr); gap: var(--kfc-space-12); align-items: start; } }
.product-page-photo { aspect-ratio: var(--w-card-media); max-height: 176px; display: flex; align-items: center; justify-content: center; background: var(--kfc-white); }
@media (min-width: 600px) { .product-page-photo { max-height: 300px; } }
@media (min-width: 1136px) { .product-page-photo { max-height: none; } }
.product-page-photo img { width: 100%; height: 100%; object-fit: contain; }
.product-page-detail h1 { overflow-wrap: anywhere; }
.product-page-price { font-weight: 400; font-size: 22px; line-height: 28px; margin: var(--w-gap-tight) 0 var(--kfc-space-1); }
.product-page-desc { color: var(--kfc-text-secondary); margin-bottom: var(--w-gap-tight); }
/* One compact notice line, not two rows and a 48px tap target: where this price is from,
   whether that restaurant is taking orders, and the way to change it — all on one baseline. */
.product-page-where {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px var(--w-gap-tight);
    margin: 0; padding-bottom: var(--w-gap-tight); border-bottom: var(--kfc-border-300) solid var(--kfc-black);
}
.product-page-where .text-link { min-height: 0; margin-left: auto; }
.product-page-form .group:first-of-type { margin-top: var(--w-gap-inline); }
@media (min-width: 600px) { .product-page-form .group:first-of-type { margin-top: var(--kfc-space-6); } }
.product-page-form .option-qty { width: 72px; min-height: var(--tap); padding: 0 var(--kfc-space-2); text-align: center;
    border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40); background: var(--kfc-white); }
.product-page-add { display: flex; align-items: flex-end; gap: var(--kfc-space-4); margin-top: var(--kfc-space-8);
    padding-top: var(--kfc-space-5); border-top: var(--kfc-border-100) solid var(--kfc-grey-200); }
.product-page-add .qty-field { margin: 0; flex: none; width: 96px; }
.product-page-add .btn { flex: 1; }
.category-back { margin-top: var(--kfc-space-10); }

/* ------------------------------------------------------------------ checkout field errors (B3)
   The message lives inside the .field it names, under the input, with the input marked
   aria-invalid and outlined in Error Main. #place-errors keeps only errors that name no field. */
.field-error { margin: var(--kfc-space-1) 0 0; font-size: 14px; line-height: 20px; }
.field input[aria-invalid="true"], .field select[aria-invalid="true"], .field textarea[aria-invalid="true"] {
    border-color: var(--kfc-error-main); border-width: 2px;
}
/* P1-6: the heavy double outline. A focused invalid field drew a 2px red box, a 2px white gap
   and a 2.5px black ring around that — three concentric boundaries on an empty Name field. The
   ring stays (it is the site's one focus indicator and it is 21:1) but it sits ON the red edge
   instead of outside a gap, so the field reads as one two-tone boundary. */
.field input[aria-invalid="true"]:focus, .field select[aria-invalid="true"]:focus, .field textarea[aria-invalid="true"]:focus {
    outline-offset: 0;
}

/* A completed step, collapsed to one line with a way back into it (B4). */
.step-summary { display: flex; align-items: baseline; gap: var(--kfc-space-3); margin: 0 0 var(--kfc-space-6);
    padding: var(--kfc-space-3) 0; border-top: var(--kfc-border-100) solid var(--kfc-grey-200);
    border-bottom: var(--kfc-border-100) solid var(--kfc-grey-200); font-size: 14px; line-height: 20px; }
.step-summary[hidden] { display: none; }
.step-summary p { margin: 0; flex: 1; min-width: 0; }
.step-summary .link-button { flex: none; min-height: 0; }
#payment-heading:focus-visible { outline-offset: 6px; }

/* Menu search: one input and one button, on the grid. Server-side (GET /menu?q=). */
.menu-search { display: flex; gap: var(--kfc-space-2); margin-top: var(--kfc-space-4); max-width: 520px; }
.menu-search input { flex: 1; min-width: 0; min-height: var(--tap); padding: 10px var(--kfc-space-3);
    border: var(--kfc-border-100) solid var(--kfc-control-line); border-radius: var(--kfc-radius-40);
    background: var(--kfc-white); font-size: 16px; font-weight: 400; }
.menu-search input:hover { border-color: var(--kfc-black); }
.menu-search .btn { flex: none; }
