/*!
 * JadTags — the design system.
 *
 * Hand-written, served as-is. No build step, no preprocessor, no framework to
 * fight: Bootstrap provides the grid and the components, and everything below
 * gives them a voice.
 *
 * Written with logical properties throughout — inline-start, not left. The
 * dashboard is Arabic by default and a restaurant working in English gets the
 * same stylesheet mirrored, rather than a second copy of it that drifts. The
 * old system shipped two pre-flipped asset trees of 25 MB and 324 places that
 * interpolated a directory name to choose between them.
 */

/* ---------------------------------------------------------------------------
 * Typeface
 *
 * IBM Plex Sans Arabic, self-hosted. Not a link to Google: the dashboard has
 * to open with no internet, and a restaurant's visitors should not be
 * announced to a third party on their way to reading a menu.
 *
 * Subset to Arabic and Latin. The Cyrillic, Greek and Vietnamese cuts are
 * bytes nobody in this market will render.
 * ------------------------------------------------------------------------ */

@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-arabic-400.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-ext-400.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-arabic-500.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-ext-500.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-500.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-arabic-600.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-ext-600.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-600.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-arabic-700.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-ext-700.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Plex Arabic';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../vendor/fonts/plex-arabic-latin-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------------------------------------------------------------------------
 * Tokens
 *
 * A warm palette, not the cool grey-blue every admin panel defaults to. This
 * is a product about food, and the people using it spend their day in it.
 *
 * The accent is a deep olive-teal: it reads as considered rather than
 * corporate, it sits well against food photography without competing with it,
 * and it stays legible on both grounds. Every colour is defined once here, on
 * bare :root — the dark block below redefines only what changes, so a value
 * can never exist in one theme and be missing from the other.
 * ------------------------------------------------------------------------ */

:root {
  /* Ground and surfaces */
  --jt-bg:            #F6F4F0;
  --jt-surface:       #FFFFFF;
  --jt-surface-sunk:  #EFEBE4;
  --jt-line:          #E2DCD2;
  --jt-line-strong:   #CFC7B9;

  /* Ink */
  --jt-ink:           #1C1A17;
  --jt-body:          #4A453D;
  --jt-muted:         #857E72;

  /* Accent */
  --jt-accent:        #0F5E58;
  --jt-accent-hover:  #0B4A45;
  --jt-accent-soft:   #E3EEEC;
  --jt-accent-ink:    #FFFFFF;

  /* Meaning */
  --jt-ok:            #1F7A4D;
  --jt-ok-soft:       #DFF0E6;
  --jt-warn:          #96631A;
  --jt-warn-soft:     #FAEEDB;
  --jt-danger:        #A83232;
  --jt-danger-soft:   #F8E5E4;

  /* Form */
  --jt-radius:        10px;
  --jt-radius-lg:     16px;
  --jt-shadow-sm:     0 1px 2px rgb(28 26 23 / .05), 0 1px 3px rgb(28 26 23 / .04);
  --jt-shadow:        0 2px 4px rgb(28 26 23 / .04), 0 8px 24px -8px rgb(28 26 23 / .10);
  --jt-shadow-lg:     0 4px 8px rgb(28 26 23 / .04), 0 24px 48px -16px rgb(28 26 23 / .16);

  --jt-sans: 'Plex Arabic', 'Noto Sans Arabic', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Optical sizes. Arabic sits lower and needs more line height than Latin at
     the same point size; these are tuned for it rather than inherited. */
  --jt-leading-tight: 1.35;
  --jt-leading:       1.75;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --jt-bg:            #131211;
    --jt-surface:       #1B1917;
    --jt-surface-sunk:  #221F1C;
    --jt-line:          #2E2A26;
    --jt-line-strong:   #443E37;

    --jt-ink:           #F2EFEA;
    --jt-body:          #C6BFB5;
    --jt-muted:         #8E867A;

    --jt-accent:        #4FB0A6;
    --jt-accent-hover:  #6AC3BA;
    --jt-accent-soft:   #10302D;
    --jt-accent-ink:    #0A1F1D;

    --jt-ok:            #5CC48D;
    --jt-ok-soft:       #122C1E;
    --jt-warn:          #D8A44B;
    --jt-warn-soft:     #2E2312;
    --jt-danger:        #E08D8A;
    --jt-danger-soft:   #331B1A;

    --jt-shadow-sm:     0 1px 2px rgb(0 0 0 / .3);
    --jt-shadow:        0 2px 4px rgb(0 0 0 / .25), 0 8px 24px -8px rgb(0 0 0 / .5);
    --jt-shadow-lg:     0 4px 8px rgb(0 0 0 / .3), 0 24px 48px -16px rgb(0 0 0 / .6);
  }
}

[data-theme="dark"] {
  --jt-bg:            #131211;
  --jt-surface:       #1B1917;
  --jt-surface-sunk:  #221F1C;
  --jt-line:          #2E2A26;
  --jt-line-strong:   #443E37;
  --jt-ink:           #F2EFEA;
  --jt-body:          #C6BFB5;
  --jt-muted:         #8E867A;
  --jt-accent:        #4FB0A6;
  --jt-accent-hover:  #6AC3BA;
  --jt-accent-soft:   #10302D;
  --jt-accent-ink:    #0A1F1D;
  --jt-ok:            #5CC48D;
  --jt-ok-soft:       #122C1E;
  --jt-warn:          #D8A44B;
  --jt-warn-soft:     #2E2312;
  --jt-danger:        #E08D8A;
  --jt-danger-soft:   #331B1A;
  --jt-shadow-sm:     0 1px 2px rgb(0 0 0 / .3);
  --jt-shadow:        0 2px 4px rgb(0 0 0 / .25), 0 8px 24px -8px rgb(0 0 0 / .5);
  --jt-shadow-lg:     0 4px 8px rgb(0 0 0 / .3), 0 24px 48px -16px rgb(0 0 0 / .6);
}

/* Bootstrap speaks through our tokens rather than beside them, so a component
   we have not restyled still belongs to the same page. */
:root {
  --bs-body-bg:        var(--jt-bg);
  --bs-body-color:     var(--jt-body);
  --bs-body-font-family: var(--jt-sans);
  --bs-border-color:   var(--jt-line);
  --bs-primary:        var(--jt-accent);
  --bs-link-color:     var(--jt-accent);
  --bs-link-hover-color: var(--jt-accent-hover);
  --bs-border-radius:  var(--jt-radius);
  --bs-border-radius-lg: var(--jt-radius-lg);
  --bs-focus-ring-color: color-mix(in oklab, var(--jt-accent) 30%, transparent);
}

/* ---------------------------------------------------------------------------
 * Base
 * ------------------------------------------------------------------------ */

* { box-sizing: border-box; }

body {
  background: var(--jt-bg);
  color: var(--jt-body);
  font-family: var(--jt-sans);
  font-size: 15px;
  line-height: var(--jt-leading);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Arabic numerals inside Arabic text should not jump between widths as a
     total updates; tabular figures keep a price column still. */
  font-variant-numeric: tabular-nums;
}

h1, h2, h3, h4, h5, h6, .jt-display {
  color: var(--jt-ink);
  font-weight: 650;
  line-height: var(--jt-leading-tight);
  letter-spacing: -0.011em;
  text-wrap: balance;
}

a { text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

::selection { background: var(--jt-accent-soft); color: var(--jt-ink); }

:focus-visible {
  outline: 2px solid var(--jt-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

/* ---------------------------------------------------------------------------
 * Application shell
 *
 * A fixed sidebar on wide screens, a drawer below. Written with inline-start
 * so it lands on the right in Arabic and the left in English without a second
 * stylesheet.
 * ------------------------------------------------------------------------ */

.jt-shell { display: flex; min-height: 100vh; }

.jt-side {
  flex: 0 0 16rem;
  background: var(--jt-surface);
  border-inline-end: 1px solid var(--jt-line);
  padding: 1.25rem 0.875rem;
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

.jt-main { flex: 1 1 auto; min-width: 0; }

.jt-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in oklab, var(--jt-bg) 88%, transparent);
  backdrop-filter: saturate(1.6) blur(12px);
  border-block-end: 1px solid var(--jt-line);
  padding: 0.75rem 1.5rem;
  display: flex;
  align-items: center;
  gap: 1rem;
}

.jt-content { padding: 2rem 1.5rem 5rem; max-width: 64rem; }

/* Brand */
.jt-brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.25rem 0.5rem 1rem;
  margin-block-end: 0.75rem;
  border-block-end: 1px solid var(--jt-line);
  color: var(--jt-ink);
}
.jt-brand:hover { text-decoration: none; }
.jt-brand-mark {
  flex: none;
  width: 2rem; height: 2rem;
  border-radius: 8px;
  background: var(--jt-accent);
  color: var(--jt-accent-ink);
  display: grid; place-items: center;
  font-weight: 700; font-size: 0.9rem;
  overflow: hidden;
}
.jt-brand-mark img { width: 100%; height: 100%; object-fit: cover; }
.jt-brand-text { min-width: 0; }
.jt-brand-name {
  display: block; font-weight: 650; font-size: 0.95rem; color: var(--jt-ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.jt-brand-sub { display: block; font-size: 0.72rem; color: var(--jt-muted); letter-spacing: .04em; }

/* Navigation */
.jt-nav { display: flex; flex-direction: column; gap: 1px; }
.jt-nav-label {
  font-size: 0.68rem; font-weight: 650; letter-spacing: .1em;
  color: var(--jt-muted); padding: 1rem 0.6rem 0.35rem;
}
.jt-nav a {
  display: flex; align-items: center; gap: 0.6rem;
  padding: 0.5rem 0.6rem;
  border-radius: var(--jt-radius);
  color: var(--jt-body);
  font-size: 0.9rem;
  transition: background .12s ease, color .12s ease;
}
.jt-nav a:hover { background: var(--jt-surface-sunk); color: var(--jt-ink); text-decoration: none; }
.jt-nav a.is-active { background: var(--jt-accent-soft); color: var(--jt-accent); font-weight: 600; }
.jt-nav svg { flex: none; width: 1.05rem; height: 1.05rem; stroke-width: 1.7; }
.jt-nav .jt-nav-count { margin-inline-start: auto; font-size: .75rem; color: var(--jt-muted); }

.jt-side-foot { margin-block-start: auto; padding-block-start: 1rem; border-block-start: 1px solid var(--jt-line); }

/* ---------------------------------------------------------------------------
 * Page furniture
 * ------------------------------------------------------------------------ */

.jt-page-head { margin-block-end: 2rem; }
.jt-page-head h1 { font-size: 1.6rem; margin: 0 0 .3rem; }
.jt-page-head p { color: var(--jt-muted); margin: 0; max-width: 60ch; font-size: .92rem; }

.jt-card {
  background: var(--jt-surface);
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius-lg);
  box-shadow: var(--jt-shadow-sm);
  margin-block-end: 1.25rem;
  overflow: hidden;
}
.jt-card-head {
  padding: 1rem 1.35rem;
  border-block-end: 1px solid var(--jt-line);
  display: flex; align-items: center; gap: .75rem;
}
.jt-card-head h2 { font-size: .95rem; margin: 0; font-weight: 650; }
.jt-card-head p { margin: .1rem 0 0; font-size: .82rem; color: var(--jt-muted); }
.jt-card-body { padding: 1.35rem; }

.jt-chip {
  display: inline-flex; align-items: center; gap: .3rem;
  padding: .12rem .55rem;
  border-radius: 999px;
  font-size: .75rem; font-weight: 550;
  background: var(--jt-surface-sunk); color: var(--jt-body);
  border: 1px solid var(--jt-line);
}
.jt-chip-ok     { background: var(--jt-ok-soft);     color: var(--jt-ok);     border-color: transparent; }
.jt-chip-warn   { background: var(--jt-warn-soft);   color: var(--jt-warn);   border-color: transparent; }
.jt-chip-danger { background: var(--jt-danger-soft); color: var(--jt-danger); border-color: transparent; }
.jt-chip-accent { background: var(--jt-accent-soft); color: var(--jt-accent); border-color: transparent; }

/* ---------------------------------------------------------------------------
 * Controls
 *
 * Restyled rather than replaced: these are Bootstrap's classes, so a component
 * we have not touched still looks like it belongs.
 * ------------------------------------------------------------------------ */

.form-label {
  font-size: .84rem; font-weight: 600; color: var(--jt-ink);
  margin-block-end: .35rem;
  display: flex; align-items: baseline; gap: .5rem;
}
/* Pushed to the far end of the label line rather than jammed against the last
   letter. A red asterisk touching a word reads as an error on a field nobody
   has typed in yet; a quiet word at the opposite margin reads as a note. */
.form-label .jt-req {
  margin-inline-start: auto;
  color: var(--jt-muted);
  font-weight: 400;
  font-size: .72rem;
  letter-spacing: .02em;
}

.form-control, .form-select {
  background: var(--jt-surface);
  border: 1px solid var(--jt-line-strong);
  border-radius: var(--jt-radius);
  color: var(--jt-ink);
  padding: .55rem .8rem;
  font-size: .92rem;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.form-control::placeholder { color: var(--jt-muted); }
.form-control:focus, .form-select:focus {
  background: var(--jt-surface);
  border-color: var(--jt-accent);
  color: var(--jt-ink);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--jt-accent) 18%, transparent);
}
.form-control.is-invalid, .form-select.is-invalid {
  border-color: var(--jt-danger);
  /* Bootstrap's icon assumes a left-to-right field; ours is often the other
     way round, and a warning triangle over the first character of an Arabic
     word is worse than no icon at all. */
  background-image: none;
  padding-inline-end: .8rem;
}
.form-control.is-invalid:focus, .form-select.is-invalid:focus {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--jt-danger) 18%, transparent);
}
.form-text { color: var(--jt-muted); font-size: .8rem; }
.invalid-feedback { color: var(--jt-danger); font-size: .82rem; font-weight: 500; }

.form-check-input { border-color: var(--jt-line-strong); }
.form-check-input:checked { background-color: var(--jt-accent); border-color: var(--jt-accent); }
.form-check-input:focus { box-shadow: 0 0 0 3px color-mix(in oklab, var(--jt-accent) 18%, transparent); }

.btn {
  border-radius: var(--jt-radius);
  font-weight: 600; font-size: .9rem;
  padding: .55rem 1.1rem;
  transition: transform .06s ease, background .12s ease, box-shadow .12s ease;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
  background: var(--jt-accent); border-color: var(--jt-accent); color: var(--jt-accent-ink);
  box-shadow: var(--jt-shadow-sm);
}
.btn-primary:hover, .btn-primary:focus {
  background: var(--jt-accent-hover); border-color: var(--jt-accent-hover); color: var(--jt-accent-ink);
}
.btn-primary:disabled { background: var(--jt-accent); border-color: var(--jt-accent); opacity: .55; }
.btn-outline-secondary {
  border-color: var(--jt-line-strong); color: var(--jt-body); background: var(--jt-surface);
}
.btn-outline-secondary:hover { background: var(--jt-surface-sunk); border-color: var(--jt-line-strong); color: var(--jt-ink); }

.alert { border: 1px solid transparent; border-radius: var(--jt-radius); font-size: .9rem; padding: .7rem 1rem; }
.alert-danger  { background: var(--jt-danger-soft); color: var(--jt-danger); }
.alert-success { background: var(--jt-ok-soft);     color: var(--jt-ok); }

/* A field that has just been corrected should say so without a banner. */
.form-control.is-valid, .form-select.is-valid { border-color: var(--jt-ok); background-image: none; }

/* ---------------------------------------------------------------------------
 * Tables
 * ------------------------------------------------------------------------ */

.jt-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: .9rem; }
.jt-table th {
  text-align: start; font-size: .74rem; font-weight: 650; letter-spacing: .06em;
  color: var(--jt-muted); padding: .6rem 1rem; border-block-end: 1px solid var(--jt-line);
  white-space: nowrap;
}
.jt-table td { padding: .8rem 1rem; border-block-end: 1px solid var(--jt-line); vertical-align: middle; }
.jt-table tbody tr:last-child td { border-block-end: 0; }
.jt-table tbody tr { transition: background .1s ease; }
.jt-table tbody tr:hover { background: var(--jt-surface-sunk); }
.jt-table-wrap { overflow-x: auto; }

.jt-empty {
  padding: 3.5rem 2rem; text-align: center; color: var(--jt-muted);
}
.jt-empty svg { width: 2.5rem; height: 2.5rem; stroke-width: 1.4; opacity: .5; margin-block-end: .75rem; }
.jt-empty h3 { font-size: 1rem; margin: 0 0 .35rem; }
.jt-empty code {
  display: inline-block; margin-block-start: .75rem;
  background: var(--jt-surface-sunk); border: 1px solid var(--jt-line);
  padding: .4rem .7rem; border-radius: var(--jt-radius);
  font-size: .82rem; color: var(--jt-ink); direction: ltr;
}

/* ---------------------------------------------------------------------------
 * Sign in
 *
 * A split screen: the form on one side, the restaurant's own identity on the
 * other. The point of the second half is that this is *their* dashboard, not
 * a login box that could belong to any product — the first thing an owner
 * sees each morning should be their own name.
 * ------------------------------------------------------------------------ */

.jt-auth { min-height: 100vh; display: grid; grid-template-columns: 1fr; }
@media (min-width: 62em) { .jt-auth { grid-template-columns: 1fr 1.05fr; } }

.jt-auth-form {
  display: grid; place-items: center;
  padding: 2.5rem 1.5rem;
  background: var(--jt-bg);
}
.jt-auth-inner { width: 100%; max-width: 23rem; }

.jt-auth-art {
  display: none;
  position: relative;
  padding: 4rem 3.5rem;
  align-content: center;
  background:
    radial-gradient(100% 70% at 50% 0%, color-mix(in oklab, var(--jt-accent) 16%, transparent), transparent 70%),
    radial-gradient(80% 60% at 100% 100%, color-mix(in oklab, var(--jt-warn) 10%, transparent), transparent 60%),
    var(--jt-surface);
  border-inline-start: 1px solid var(--jt-line);
  overflow: hidden;
}
@media (min-width: 62em) { .jt-auth-art { display: grid; } }

/* A plate, drawn in CSS rather than shipped as an image: it scales, it
   recolours with the theme, and it costs no request.
 *
 * Positioned with logical properties. Written as `right` and `bottom` it
 * landed in the middle of the panel once the page mirrored into Arabic — a
 * cropped arc that read as a rendering fault rather than as decoration. */
.jt-auth-art::before,
.jt-auth-art::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}

/* The rim. Deliberately mostly off-canvas: a whole circle would be a logo,
   and this is meant to be texture. */
.jt-auth-art::before {
  inset-block-end: -14rem;
  inset-inline-start: -10rem;
  width: 32rem; height: 32rem;
  border: 1.5px solid color-mix(in oklab, var(--jt-accent) 26%, transparent);
  box-shadow: inset 0 0 0 2.5rem color-mix(in oklab, var(--jt-accent) 7%, transparent);
}

.jt-auth-art::after {
  inset-block-start: -9rem;
  inset-inline-end: -7rem;
  width: 20rem; height: 20rem;
  border: 1.5px solid color-mix(in oklab, var(--jt-accent) 18%, transparent);
}

.jt-auth-art-body { position: relative; max-width: 27rem; }
.jt-auth-logo {
  width: 4.5rem; height: 4.5rem; border-radius: 18px;
  background: var(--jt-surface); border: 1px solid var(--jt-line);
  box-shadow: var(--jt-shadow);
  display: grid; place-items: center; overflow: hidden;
  margin-block-end: 1.5rem;
}
.jt-auth-logo img { width: 100%; height: 100%; object-fit: cover; }
.jt-auth-art h2 { font-size: 2.1rem; margin: 0 0 .6rem; letter-spacing: -.025em; }
.jt-auth-art p { color: var(--jt-body); font-size: 1rem; margin: 0; max-width: 32ch; line-height: 1.7; }

.jt-auth-points { list-style: none; margin: 2.5rem 0 0; padding: 0; display: grid; gap: .85rem; }
.jt-auth-points li { display: flex; gap: .7rem; align-items: start; font-size: .9rem; color: var(--jt-body); }
.jt-auth-points svg { flex: none; width: 1.1rem; height: 1.1rem; margin-block-start: .3rem; color: var(--jt-accent); }

.jt-auth-mark {
  display: inline-flex; align-items: center; gap: .5rem;
  font-size: .75rem; font-weight: 650; letter-spacing: .12em;
  color: var(--jt-muted); margin-block-end: 2rem;
}
.jt-auth-mark i {
  width: 1.35rem; height: 1.35rem; border-radius: 6px; font-style: normal;
  background: var(--jt-accent); color: var(--jt-accent-ink);
  display: grid; place-items: center; font-size: .7rem;
}

.jt-auth h1 { font-size: 1.5rem; margin: 0 0 .35rem; }
.jt-auth-lede { color: var(--jt-muted); font-size: .9rem; margin-block-end: 1.75rem; }
.jt-auth-foot { margin-block-start: 1.75rem; font-size: .82rem; color: var(--jt-muted); text-align: center; }

/* ---------------------------------------------------------------------------
 * Platform (the central site)
 * ------------------------------------------------------------------------ */

.jt-platform { max-width: 68rem; margin-inline: auto; padding: 4rem 1.5rem 6rem; }
.jt-hero { margin-block-end: 3rem; }
.jt-hero h1 { font-size: 2.6rem; letter-spacing: -.03em; margin: 0 0 .6rem; }
.jt-hero p { font-size: 1.05rem; color: var(--jt-muted); margin: 0; max-width: 52ch; }

.jt-stats { display: grid; gap: .75rem; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); margin-block: 2rem; }
.jt-stat {
  background: var(--jt-surface); border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius-lg); padding: 1rem 1.15rem;
}
.jt-stat b { display: block; font-size: 1.7rem; font-weight: 700; color: var(--jt-ink); line-height: 1.2; }
.jt-stat span { font-size: .78rem; color: var(--jt-muted); }

.jt-avatar {
  width: 2.25rem; height: 2.25rem; border-radius: 9px; flex: none;
  background: var(--jt-accent-soft); color: var(--jt-accent);
  display: grid; place-items: center; font-weight: 700; font-size: .82rem;
  overflow: hidden;
}
.jt-avatar img { width: 100%; height: 100%; object-fit: cover; }

/* ---------------------------------------------------------------------------
 * Small screens
 * ------------------------------------------------------------------------ */

@media (max-width: 61.99em) {
  .jt-side {
    position: fixed; inset-block: 0; inset-inline-start: 0;
    z-index: 60; width: 16rem;
    transform: translateX(var(--jt-drawer-out, -110%));
    transition: transform .2s ease;
    box-shadow: var(--jt-shadow-lg);
  }
  [dir="rtl"] .jt-side { --jt-drawer-out: 110%; }
  .jt-shell.is-open .jt-side { transform: translateX(0); }
  .jt-scrim {
    position: fixed; inset: 0; z-index: 50;
    background: rgb(0 0 0 / .4);
    opacity: 0; pointer-events: none; transition: opacity .2s ease;
  }
  .jt-shell.is-open .jt-scrim { opacity: 1; pointer-events: auto; }
  .jt-content { padding: 1.5rem 1rem 4rem; }
}
@media (min-width: 62em) { .jt-burger, .jt-scrim { display: none; } }

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: .01ms !important; animation-duration: .01ms !important; }
}

/* ---------------------------------------------------------------------------
 * Image field
 *
 * The browser's own file control is the one element on the page that cannot be
 * styled and cannot be translated — it says "Choose File" in the browser's
 * language, not the restaurant's, and no CSS reaches it. So it is hidden and
 * driven by a label, which is a real click target for both mouse and keyboard
 * and needs no JavaScript to work.
 * ------------------------------------------------------------------------ */

.jt-file { display: flex; align-items: center; gap: 1rem; }

.jt-file input[type="file"] {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.jt-file-preview {
  flex: none;
  width: 4.5rem; height: 4.5rem;
  border-radius: var(--jt-radius);
  border: 1px dashed var(--jt-line-strong);
  background: var(--jt-surface-sunk);
  display: grid; place-items: center;
  overflow: hidden;
  color: var(--jt-muted);
}
.jt-file-preview img { width: 100%; height: 100%; object-fit: cover; }
.jt-file-preview.has-image { border-style: solid; border-color: var(--jt-line); }

.jt-file-action {
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .45rem .9rem;
  border: 1px solid var(--jt-line-strong);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  color: var(--jt-body);
  font-size: .86rem; font-weight: 600;
  cursor: pointer;
  transition: background .12s ease, border-color .12s ease;
}
.jt-file-action:hover { background: var(--jt-surface-sunk); border-color: var(--jt-accent); color: var(--jt-ink); }
.jt-file input[type="file"]:focus-visible + .jt-file-body .jt-file-action {
  outline: 2px solid var(--jt-accent); outline-offset: 2px;
}
.jt-file-name {
  display: block; font-size: .8rem; color: var(--jt-muted);
  margin-block-start: .4rem; word-break: break-all;
}

/* ---------------------------------------------------------------------------
 * Toast
 *
 * Confirmation has to be where the eye already is.
 *
 * A banner at the top of the form is invisible to somebody who has just
 * pressed Save at the bottom of a long one: the page does not move, nothing
 * appears in view, and they press it again. Scrolling them back up would work
 * but takes the screen away from whatever they were about to do next. A fixed
 * toast is seen wherever they are standing.
 * ------------------------------------------------------------------------ */

.jt-toasts {
  position: fixed;
  inset-block-start: 1rem;
  inset-inline: 0;
  z-index: 1080;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .5rem;
  pointer-events: none;
  padding-inline: 1rem;
}

.jt-toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: .6rem;
  max-width: min(28rem, 100%);
  padding: .7rem 1.1rem;
  border-radius: 999px;
  background: var(--jt-surface);
  border: 1px solid var(--jt-line);
  box-shadow: var(--jt-shadow-lg);
  color: var(--jt-ink);
  font-size: .9rem; font-weight: 550;
  animation: jt-toast-in .22s cubic-bezier(.2, .8, .2, 1);
}
.jt-toast svg { flex: none; }
.jt-toast-ok     { border-color: color-mix(in oklab, var(--jt-ok) 35%, transparent); }
.jt-toast-ok     svg { color: var(--jt-ok); }
.jt-toast-danger { border-color: color-mix(in oklab, var(--jt-danger) 35%, transparent); }
.jt-toast-danger svg { color: var(--jt-danger); }

.jt-toast.is-leaving { animation: jt-toast-out .18s ease forwards; }

@keyframes jt-toast-in  { from { opacity: 0; transform: translateY(-.5rem) scale(.97); } }
@keyframes jt-toast-out { to   { opacity: 0; transform: translateY(-.4rem); } }

@media (prefers-reduced-motion: reduce) {
  .jt-toast, .jt-toast.is-leaving { animation: none; }
}

/* ---------------------------------------------------------------------------
 * Language switcher
 * ------------------------------------------------------------------------ */

.jt-lang { position: relative; }
.jt-lang > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .35rem .7rem;
  border: 1px solid var(--jt-line-strong);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  color: var(--jt-body); font-size: .84rem; font-weight: 600;
}
.jt-lang > summary::-webkit-details-marker { display: none; }
.jt-lang > summary:hover { background: var(--jt-surface-sunk); color: var(--jt-ink); }
.jt-lang-menu {
  position: absolute; inset-block-start: calc(100% + .35rem); inset-inline-end: 0;
  z-index: 40; min-width: 10rem;
  background: var(--jt-surface);
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  box-shadow: var(--jt-shadow);
  padding: .3rem;
}
.jt-lang-menu a {
  display: flex; align-items: center; gap: .5rem;
  padding: .4rem .6rem; border-radius: 7px;
  color: var(--jt-body); font-size: .88rem;
}
.jt-lang-menu a:hover { background: var(--jt-surface-sunk); color: var(--jt-ink); text-decoration: none; }
.jt-lang-menu a.is-active { color: var(--jt-accent); font-weight: 650; }
.jt-lang-menu a.is-active::after { content: '✓'; margin-inline-start: auto; }

/* Language picker in settings */
.jt-lang-option {
  display: flex; align-items: center; gap: .5rem;
  padding: .6rem .75rem;
  border: 1px solid var(--jt-line-strong);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  cursor: pointer;
  transition: border-color .12s ease, background .12s ease;
  height: 100%;
}
.jt-lang-option:hover { border-color: var(--jt-accent); }
.jt-lang-option:has(input:checked) { border-color: var(--jt-accent); background: var(--jt-accent-soft); }
.jt-lang-option:has(input:disabled) { opacity: .75; cursor: default; }
.jt-lang-option:has(input:focus-visible) { outline: 2px solid var(--jt-accent); outline-offset: 2px; }
.jt-lang-option input { accent-color: var(--jt-accent); flex: none; }
.jt-lang-option-name { color: var(--jt-ink); font-size: .88rem; font-weight: 550; }
.jt-lang-option-code { margin-inline-start: auto; font-size: .72rem; color: var(--jt-muted); text-transform: uppercase; }

/* ---------------------------------------------------------------------------
 * The public menu
 *
 * Built for one number: how long this takes to open on a phone, on the
 * restaurant's wifi, while somebody is waiting to order.
 * ------------------------------------------------------------------------ */

.jt-menu { max-width: 60rem; margin-inline: auto; padding: 0 1rem 5rem; }

.jt-menu-head {
  display: flex; align-items: center; gap: 1rem;
  padding: 2rem 0 1.5rem;
}
.jt-menu-logo {
  width: 3.5rem; height: 3.5rem; flex: none;
  border-radius: 14px; overflow: hidden;
  background: var(--jt-surface); border: 1px solid var(--jt-line);
  display: grid; place-items: center;
}
.jt-menu-logo img { width: 100%; height: 100%; object-fit: cover; }
.jt-menu-head h1 { font-size: 1.5rem; margin: 0; }
.jt-menu-head p { margin: .1rem 0 0; color: var(--jt-muted); font-size: .88rem; }

/* A page that is nothing but prose — terms, privacy, the accessibility
   statement. Not a card: a card is a thing among other things, and this is the
   only thing on the screen. 62ch is the measure, so a policy on a laptop does
   not run to a line nobody can track back from. The child rules are the
   operator's own HTML, which arrives with no classes of its own. */
.jt-prose { max-width: 62ch; line-height: 1.7; padding-bottom: 1.5rem; }
.jt-prose > * { margin: 0; }
.jt-prose > * + * { margin-block-start: .9rem; }
.jt-prose > h2, .jt-prose > h3 { margin-block-start: 1.6rem; }
.jt-prose h2 { font-size: 1.15rem; }
.jt-prose h3 { font-size: 1rem; }
.jt-prose ul, .jt-prose ol { padding-inline-start: 1.4rem; }
.jt-prose li + li { margin-block-start: .35rem; }

/* The section strip. Scrolls horizontally on a phone rather than wrapping
   into three lines that push the food off the screen. */
/* Ordering is on but shut right now — said plainly, above the menu. */
.jt-notice {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-block: 0 1.25rem;
    padding: .7rem .9rem;
    border: 1px solid var(--jt-line);
    border-inline-start: 3px solid var(--jt-accent);
    border-radius: .5rem;
    background: var(--jt-surface, #fff);
    color: var(--jt-muted);
    font-size: .88rem;
}
.jt-notice svg { flex: none; color: var(--jt-accent); }

/* The 86 board's inline "take it off sale" control. */
.jt-mark > summary { list-style: none; cursor: pointer; display: inline-flex; }
.jt-mark > summary::-webkit-details-marker { display: none; }
.jt-mark[open] > summary { visibility: hidden; height: 0; }
.jt-mark-body {
    min-inline-size: 15rem;
    padding: .75rem 0 .25rem;
}
.jt-mark-body .form-label { font-size: .8rem; }

/* Report tiles, the by-hour bars and the service split. */
.jt-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: .75rem;
    margin-block-end: 1rem;
}
.jt-tile {
    padding: .85rem 1rem;
    border: 1px solid var(--jt-line);
    border-radius: var(--jt-radius);
    background: var(--jt-surface, #fff);
}
.jt-tile.is-accent { border-color: var(--jt-accent); }
.jt-tile-label { display: block; font-size: .76rem; color: var(--jt-muted); }
.jt-tile-value { display: block; font-size: 1.45rem; line-height: 1.2; margin-block-start: .15rem; }
.jt-tile-delta { font-size: .78rem; color: #1a7f5a; }
.jt-tile-delta.is-down { color: #b23c3c; }

.jt-bars {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    block-size: 7rem;
}
.jt-bar { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; block-size: 100%; }
.jt-bar > span {
    display: block;
    background: var(--jt-accent);
    border-radius: 2px 2px 0 0;
    min-block-size: 0;
}
.jt-bar > em {
    font-style: normal;
    font-size: .65rem;
    color: var(--jt-muted);
    text-align: center;
    block-size: 1rem;
}

.jt-split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    gap: 1rem;
}

/* An attached copy, collapsed until somebody wants to edit it.
   The screen used to open every group at once: four groups was forty live
   controls, which reads as a spreadsheet rather than a menu. */
.jt-copy > summary { list-style: none; cursor: pointer; }
.jt-copy > summary::-webkit-details-marker { display: none; }
.jt-copy > summary::after {
    content: '▾'; margin-inline-start: auto; color: var(--jt-muted);
    transition: transform .12s ease;
}
.jt-copy[open] > summary::after { transform: rotate(180deg); }
.jt-copy > summary:hover { background: var(--jt-surface-sunk); }
.jt-copy-summary { color: var(--jt-muted); font-size: .82rem; }

/* The preview of the customer's own sheet, beside the editor. */
.jt-preview { border: 1px dashed var(--jt-line-strong); border-radius: var(--jt-radius); padding: 1rem; }
.jt-preview h4 { font-size: .82rem; color: var(--jt-muted); margin-block-end: .5rem; }
.jt-preview-group { margin-block-end: .9rem; }
.jt-preview-group > b { display: block; font-size: .88rem; margin-block-end: .3rem; }
.jt-preview-opt {
    display: flex; gap: .5rem; align-items: baseline;
    font-size: .85rem; color: var(--jt-body); padding-block: .15rem;
}
.jt-preview-opt .jt-delta { margin-inline-start: auto; color: var(--jt-muted); }
.jt-preview-opt.is-off { opacity: .45; text-decoration: line-through; }

/* A refusal, inside the sheet — the top layer makes a toast invisible here. */
.jt-sheet-error {
    margin: 0 1rem .5rem;
    padding: .6rem .8rem;
    border-radius: .5rem;
    background: color-mix(in srgb, var(--jt-danger, #b23c3c) 18%, transparent);
    border: 1px solid var(--jt-danger, #b23c3c);
    color: var(--jt-ink);
    font-size: .86rem;
}
.jt-choice.is-capped { opacity: .4; }

/* A dish's photographs. The first one is the card image, so the order is
   worth arranging — hence a grid you can drag in rather than a list. */
.jt-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: .75rem;
}
.jt-shot {
    position: relative;
    margin: 0;
    border: 1px solid var(--jt-line);
    border-radius: var(--jt-radius);
    overflow: hidden;
    background: var(--jt-surface-sunk);
}
.jt-shot.is-primary { border-color: var(--jt-accent); box-shadow: 0 0 0 1px var(--jt-accent); }
.jt-shot img { display: block; inline-size: 100%; block-size: 9.5rem; object-fit: cover; }
.jt-shot-badge {
    position: absolute; inset-block-start: .4rem; inset-inline-start: .4rem;
    padding: .15rem .5rem; border-radius: 999px;
    background: var(--jt-accent); color: #06231f;
    font-size: .7rem; font-weight: 700;
}
.jt-shot figcaption {
    display: flex; gap: .35rem; flex-wrap: wrap;
    padding: .5rem; border-block-start: 1px solid var(--jt-line);
}
.jt-shot figcaption .btn { padding: .2rem .5rem; font-size: .74rem; }
.jt-shot[draggable="true"] { cursor: grab; }
.jt-shot.is-dragging { opacity: .4; }

/* A curated row above the menu. `carousel` scrolls sideways, which on a phone
   is a swipe the browser already knows how to do — no script, so the row never
   collapses to one item because a file failed to load. */
.jt-shelf { margin-block-end: 2rem; }
.jt-shelf-head { margin-block-end: .75rem; }
.jt-shelf-head h2 { margin: 0; font-size: 1.1rem; }
.jt-shelf-head p { margin: .15rem 0 0; color: var(--jt-muted); font-size: .85rem; }

.jt-shelf-rail { display: grid; gap: .75rem; }
.jt-shelf-grid .jt-shelf-rail,
.jt-shelf-list .jt-shelf-rail { grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); }
.jt-shelf-list .jt-shelf-rail { grid-template-columns: 1fr; }

.jt-shelf-carousel .jt-shelf-rail {
    grid-auto-flow: column;
    grid-auto-columns: minmax(15rem, 18rem);
    overflow-x: auto;
    scroll-snap-type: inline mandatory;
    padding-block-end: .5rem;
    /* The scrollbar is noise on a touch device and a nuisance on a laptop. */
    scrollbar-width: thin;
}
.jt-shelf-carousel .jt-dish-cell { scroll-snap-align: start; }

/* The shelf picker: what is on the shelf, and what could be. */
.jt-two-pane { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; align-items: start; }
@media (max-width: 60rem) { .jt-two-pane { grid-template-columns: 1fr; } }

.jt-pin {
    display: flex; gap: .5rem; align-items: center; flex-wrap: wrap;
    padding: .5rem; margin-block-end: .4rem;
    border: 1px solid var(--jt-line); border-radius: .5rem;
    background: var(--jt-surface-sunk);
}
.jt-pin .jt-row-title { flex: 1; min-inline-size: 6rem; }
.jt-pin .form-control, .jt-pin .form-select { inline-size: auto; }

.jt-candidates { display: grid; gap: .3rem; max-block-size: 26rem; overflow-y: auto; }
.jt-candidate {
    display: block; inline-size: 100%; text-align: start;
    padding: .5rem .6rem; border: 1px solid var(--jt-line); border-radius: .5rem;
    background: none; color: inherit; font: inherit; cursor: pointer;
}
.jt-candidate:hover { border-color: var(--jt-accent); }

.jt-strip {
  display: flex; gap: .5rem;
  overflow-x: auto;
  padding: .25rem 0 1rem;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.jt-strip::-webkit-scrollbar { display: none; }
.jt-strip a {
  flex: none; scroll-snap-align: start;
  display: inline-flex; align-items: center; gap: .45rem;
  padding: .45rem .85rem;
  border: 1px solid var(--jt-line-strong);
  border-radius: 999px;
  background: var(--jt-surface);
  color: var(--jt-body); font-size: .88rem; font-weight: 550;
  white-space: nowrap;
}
.jt-strip a:hover { border-color: var(--jt-accent); text-decoration: none; }
.jt-strip a.is-active { background: var(--jt-accent); border-color: var(--jt-accent); color: var(--jt-accent-ink); }
.jt-strip img { width: 1.35rem; height: 1.35rem; border-radius: 50%; object-fit: cover; }

.jt-section { margin-block: 2.5rem 0; scroll-margin-top: 1rem; }
.jt-section > h2 { font-size: 1.15rem; margin: 0 0 .25rem; }
.jt-section > p { color: var(--jt-muted); font-size: .88rem; margin: 0 0 1rem; max-width: 60ch; }

.jt-dishes { display: grid; gap: .75rem; }
@media (min-width: 40em) { .jt-dishes { grid-template-columns: 1fr 1fr; } }

.jt-dish {
  display: flex; gap: .9rem;
  padding: .8rem;
  background: var(--jt-surface);
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius-lg);
  color: inherit;
  transition: border-color .12s ease, transform .12s ease;
}
.jt-dish:hover { border-color: var(--jt-line-strong); text-decoration: none; transform: translateY(-1px); }
.jt-dish-photo {
  flex: none; width: 5.5rem; height: 5.5rem;
  border-radius: var(--jt-radius); overflow: hidden;
  background: var(--jt-surface-sunk);
  display: grid; place-items: center; color: var(--jt-muted);
}
.jt-dish-photo img { width: 100%; height: 100%; object-fit: cover; }
.jt-dish-body { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.jt-dish-name { color: var(--jt-ink); font-weight: 600; font-size: .98rem; margin: 0 0 .15rem; }
.jt-dish-desc {
  color: var(--jt-muted); font-size: .84rem; margin: 0 0 .5rem;
  /* Two lines, then an ellipsis. A description that runs to six lines pushes
     the next dish off the screen, and nobody scrolls a menu twice. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.jt-dish-foot { margin-block-start: auto; display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

.jt-price { display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.jt-price-now { color: var(--jt-ink); font-weight: 650; font-size: 1rem; }
.jt-price-from { color: var(--jt-muted); font-size: .78rem; }
.jt-price-was { color: var(--jt-muted); font-size: .84rem; }
.jt-price-off { font-size: .7rem; }
.jt-price-note, .jt-price-free { color: var(--jt-accent); font-weight: 600; font-size: .9rem; }

.jt-dish.is-unavailable { opacity: .55; }
.jt-dish.is-unavailable .jt-dish-photo { filter: grayscale(1); }

/* Item detail */
.jt-item-hero {
  border-radius: var(--jt-radius-lg); overflow: hidden;
  background: var(--jt-surface-sunk); margin-block-end: 1.5rem;
  aspect-ratio: 16 / 10;
}
.jt-item-hero img { width: 100%; height: 100%; object-fit: cover; display: block; }
.jt-item h1 { font-size: 1.75rem; margin: 0 0 .4rem; }
.jt-item-lede { color: var(--jt-muted); font-size: 1rem; margin: 0 0 1.25rem; }
.jt-item-meta { display: flex; gap: .5rem; flex-wrap: wrap; margin-block: 1.25rem; }
.jt-sizes { display: grid; gap: .5rem; margin-block: 1.5rem; }
.jt-size {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: .7rem .9rem;
  border: 1px solid var(--jt-line-strong); border-radius: var(--jt-radius);
  background: var(--jt-surface);
}
.jt-size-name { color: var(--jt-ink); font-weight: 550; }
.jt-back { display: inline-flex; align-items: center; gap: .35rem; font-size: .88rem; color: var(--jt-muted); margin-block: 1.5rem .5rem; }

/* ---------------------------------------------------------------------------
 * Catalog screens
 * ------------------------------------------------------------------------ */

.jt-toolbar {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-block-end: 1.25rem;
}
.jt-toolbar .form-select, .jt-toolbar .form-control { width: auto; min-width: 9rem; }

.jt-row-media {
  width: 2.75rem; height: 2.75rem; flex: none;
  border-radius: 8px; overflow: hidden;
  background: var(--jt-surface-sunk);
  display: grid; place-items: center; color: var(--jt-muted);
}
.jt-row-media img { width: 100%; height: 100%; object-fit: cover; }

.jt-row-title { color: var(--jt-ink); font-weight: 600; }
.jt-row-sub { color: var(--jt-muted); font-size: .8rem; }

/* Which languages a row has been written in, at a glance. The alternative is
   opening every dish to find the one nobody translated. */
.jt-dots { display: inline-flex; gap: .2rem; margin-inline-start: .4rem; vertical-align: middle; }
.jt-dot {
  width: .4rem; height: .4rem; border-radius: 50%;
  background: var(--jt-line-strong);
}
.jt-dot.is-on { background: var(--jt-ok); }

.jt-drag {
  cursor: grab; color: var(--jt-muted); opacity: .5;
  transition: opacity .12s ease;
}
tr:hover .jt-drag { opacity: 1; }
.jt-drag:active { cursor: grabbing; }
tr.is-dragging { opacity: .4; }

/* Language tabs inside an editor */
/* Two halves of one record — NOT the language strip.
   Deliberately a segmented pill rather than an underline: the item editor
   carries a jt-tabs strip for locales a few centimetres below, and two
   identical-looking strips on one page read as one broken control. */
.jt-subnav {
  display: inline-flex;
  gap: .25rem;
  padding: .25rem;
  margin-block-end: 1.25rem;
  border: 1px solid var(--jt-line);
  border-radius: 999px;
  background: var(--jt-surface-sunk);
}
.jt-subnav-item {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .4rem .95rem; border-radius: 999px;
  color: var(--jt-muted); font-size: .88rem; font-weight: 600;
  text-decoration: none; white-space: nowrap;
}
.jt-subnav-item:hover { color: var(--jt-ink); }
.jt-subnav-item.is-active { background: var(--jt-surface); color: var(--jt-ink); box-shadow: 0 1px 2px rgb(0 0 0 / .18); }
.jt-subnav-item.is-disabled { opacity: .45; cursor: not-allowed; }
.jt-subnav-item .jt-chip { margin: 0; }

.jt-tabs { display: flex; gap: .25rem; border-block-end: 1px solid var(--jt-line); margin-block-end: 1.25rem; }
.jt-tab {
  padding: .5rem .85rem; border: 0; background: none;
  color: var(--jt-muted); font: inherit; font-size: .88rem; font-weight: 600;
  border-block-end: 2px solid transparent; margin-block-end: -1px; cursor: pointer;
}
.jt-tab:hover { color: var(--jt-ink); }
.jt-tab.is-active { color: var(--jt-accent); border-block-end-color: var(--jt-accent); }
.jt-tab-panel[hidden] { display: none; }

/* Repeating size rows */
.jt-repeat { display: grid; gap: .6rem; }
.jt-repeat-row {
  display: flex; gap: .6rem; align-items: start;
  padding: .75rem; border: 1px solid var(--jt-line); border-radius: var(--jt-radius);
  background: var(--jt-surface-sunk);
}
.jt-repeat-row .mb-3 { margin-block-end: 0 !important; flex: 1; }
.jt-repeat-remove {
  border: 0; background: none; color: var(--jt-muted); cursor: pointer;
  padding: .5rem; border-radius: 6px; margin-block-start: 1.6rem;
}
.jt-repeat-remove:hover { background: var(--jt-danger-soft); color: var(--jt-danger); }

.jt-sticky-actions {
  position: sticky; inset-block-end: 0; z-index: 10;
  background: color-mix(in oklab, var(--jt-bg) 92%, transparent);
  backdrop-filter: blur(8px);
  border-block-start: 1px solid var(--jt-line);
  padding: 1rem 0; margin-block-start: 1.5rem;
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
}

/* ==========================================================================
   The week strip
   --------------------------------------------------------------------------
   Seven tracks and bands positioned as percentages of the day. No measurement,
   no script, no fixed width — and `inset-inline-start` means the strip reads
   from the right in Arabic and Hebrew with the same markup and the same rule.

   The bands come from the derived segments, so a window that runs past
   midnight is drawn where it actually falls: the tail appears at the start of
   the following day, marked, rather than as a bar that runs off the edge.
   ========================================================================== */
.jt-week { display: grid; gap: .3rem; }

.jt-week-axis {
  position: relative; height: 1rem; margin-inline-start: 4.5rem;
  font-size: .68rem; color: var(--jt-muted); letter-spacing: .02em;
}
.jt-week-axis span { position: absolute; transform: translateX(-50%); }
[dir="rtl"] .jt-week-axis span { transform: translateX(50%); }

.jt-week-row { display: flex; align-items: center; gap: .5rem; }

.jt-week-day {
  inline-size: 4rem; flex: none; font-size: .78rem; color: var(--jt-body);
  text-align: end; white-space: nowrap;
}

.jt-week-track {
  position: relative; flex: 1; block-size: 1.6rem;
  background: var(--jt-surface-sunk);
  border: 1px solid var(--jt-line);
  border-radius: 6px; overflow: hidden;
  /* Faint marks every three hours. A gradient rather than elements, so an
     empty day still reads as a day rather than a blank box. */
  background-image: repeating-linear-gradient(
    to right, transparent 0, transparent calc(12.5% - 1px),
    var(--jt-line) calc(12.5% - 1px), var(--jt-line) 12.5%);
}

.jt-week-band {
  position: absolute; inset-block: 0;
  background: var(--jt-accent);
  display: flex; align-items: center; justify-content: center;
  min-inline-size: 2px;
  transition: filter .12s ease;
}
.jt-week-band:hover { filter: brightness(1.12); }

/* The half that belongs to the previous night, drawn hatched so it is not
   mistaken for a second, separate opening somebody authored by accident. */
.jt-week-band.is-spill {
  background: repeating-linear-gradient(
    45deg, var(--jt-accent) 0 5px, color-mix(in srgb, var(--jt-accent) 55%, transparent) 5px 10px);
}

.jt-week-band-label {
  font-size: .66rem; color: var(--jt-accent-ink);
  white-space: nowrap; padding-inline: .35rem;
  font-variant-numeric: tabular-nums;
}

.jt-week-summary {
  display: flex; align-items: center; gap: .35rem;
  margin: .5rem 0 0; margin-inline-start: 4.5rem;
  font-size: .8rem; color: var(--jt-muted);
}

.jt-week.is-compact { gap: .15rem; }
.jt-week.is-compact .jt-week-day { inline-size: 1.1rem; font-size: .66rem; }
.jt-week.is-compact .jt-week-track { block-size: .55rem; border-radius: 3px; }

@media (max-width: 34rem) {
  .jt-week-day { inline-size: 2.4rem; font-size: .7rem; overflow: hidden; }
  .jt-week-axis, .jt-week-summary { margin-inline-start: 2.9rem; }
}

/* --------------------------------------------------------------------------
   Weekday picker — seven toggles, not a multi-select
   -------------------------------------------------------------------------- */
.jt-days { display: flex; flex-wrap: wrap; gap: .3rem; }

.jt-day-toggle { position: relative; }
.jt-day-toggle input {
  position: absolute; opacity: 0; inline-size: 100%; block-size: 100%;
  margin: 0; cursor: pointer;
}
.jt-day-toggle span {
  display: grid; place-items: center;
  inline-size: 2.2rem; block-size: 2.2rem;
  border: 1px solid var(--jt-line-strong); border-radius: 50%;
  font-size: .8rem; color: var(--jt-body); background: var(--jt-surface);
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.jt-day-toggle input:checked + span {
  background: var(--jt-accent); border-color: var(--jt-accent); color: var(--jt-accent-ink);
}
/* Keyboard focus has to be visible on the circle, not on the hidden input. */
.jt-day-toggle input:focus-visible + span {
  outline: 2px solid var(--jt-accent); outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   A rule row — wider than the generic repeat row, and it wraps
   -------------------------------------------------------------------------- */
/* Overrides the generic repeat row: a rule carries days, two times and two
   dates, which is more than one flex line can hold honestly. */
.jt-repeat-row.jt-rule {
  display: grid; gap: .75rem; align-items: stretch;
  padding: .9rem; background: var(--jt-surface);
}
.jt-repeat-row.jt-rule .mb-3 { flex: none; }
.jt-rule .jt-repeat-remove { margin-block-start: 0; justify-self: end; }
.jt-repeat-row.jt-rule.is-exclude { border-inline-start: 3px solid var(--jt-warn); }
.jt-rule-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.jt-rule-head .mb-3 { margin-block-end: 0 !important; }
.jt-rule-times { display: flex; gap: .5rem; flex-wrap: wrap; align-items: flex-end; }
.jt-rule-times .mb-3 { margin-block-end: 0 !important; }
.jt-rule-times input[type="time"] { inline-size: auto; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   Availability mode — three cards, not a dropdown
   --------------------------------------------------------------------------
   A dropdown would hide two of the three answers behind a click, and the whole
   value of "same as the section" is that somebody sees it exists.
   -------------------------------------------------------------------------- */
.jt-mode-row {
  display: grid; gap: .6rem; margin-block-end: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
}

.jt-mode {
  position: relative; display: grid; gap: .2rem; cursor: pointer;
  padding: .8rem .9rem; border: 1px solid var(--jt-line-strong);
  border-radius: var(--jt-radius); background: var(--jt-surface);
  transition: border-color .12s ease, background .12s ease;
}
.jt-mode input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
/*
    The chosen card, decided by the INPUT rather than by a class the server
    printed once.

    .is-on was written into the markup at render time and nothing ever moved
    it, while the radio itself is opacity:0 — so the highlight was the only
    feedback there was, and it stayed on whichever option the page happened to
    load with. Clicking a second choice highlighted nothing and un-highlighted
    nothing.

    :has(input:checked) reads the real state, covers the server-rendered
    choice through @checked, and cannot go stale. The rule below already
    depends on :has for its focus ring, so this asks nothing new of the
    browser. .is-on is kept beside it so the six screens that still print it
    are not suddenly blank on anything without :has support.
*/
.jt-mode.is-on,
.jt-mode:has(input:checked) { border-color: var(--jt-accent); background: var(--jt-accent-soft); }
.jt-mode:has(input:focus-visible) { outline: 2px solid var(--jt-accent); outline-offset: 2px; }
.jt-mode-title { font-weight: 600; color: var(--jt-ink); font-size: .9rem; }
.jt-mode-note { font-size: .76rem; color: var(--jt-muted); line-height: 1.5; }

.jt-window-list { display: grid; gap: .5rem; }

.jt-window {
  display: flex; gap: .7rem; align-items: flex-start; cursor: pointer;
  padding: .7rem .8rem; border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius); background: var(--jt-surface-sunk);
}
.jt-window.is-on,
.jt-window:has(input:checked) { border-color: var(--jt-accent); background: var(--jt-accent-soft); }
.jt-window input { margin-block-start: .25rem; flex: none; }
.jt-window-body { display: grid; gap: .35rem; flex: 1; min-width: 0; }
.jt-window-name {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  font-weight: 600; font-size: .88rem; color: var(--jt-ink);
}
.jt-window-hours { font-size: .74rem; color: var(--jt-muted); }

/* ==========================================================================
   The dish's own copies
   --------------------------------------------------------------------------
   Each card is one frozen group or ingredient, editable in place. The
   provenance badges carry the whole model: "from the library and in step",
   "edited here", "the library moved on", "the library is gone". An operator
   who cannot see which of those they are looking at cannot use the feature at
   all.
   ========================================================================== */
.jt-copy {
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  padding: 1rem;
  margin-block-end: .75rem;
}

.jt-copy-head {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  margin-block-end: .75rem;
  padding-block-end: .6rem;
  border-block-end: 1px solid var(--jt-line);
}

.jt-copy-name {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  font-weight: 600; color: var(--jt-ink);
}
.jt-copy-name code {
  font-size: .74rem; color: var(--jt-muted);
  background: var(--jt-surface-sunk); padding: .1rem .35rem; border-radius: 4px;
}

.jt-copy-badges { display: flex; gap: .3rem; flex-wrap: wrap; margin-inline-start: auto; }

.jt-copy-actions {
  display: flex; gap: .5rem; flex-wrap: wrap;
  margin-block-start: .75rem; padding-block-start: .75rem;
  border-block-start: 1px solid var(--jt-line);
}
.jt-copy-actions form { margin: 0; }

/* --------------------------------------------------------------------------
   Options, as a grid rather than a repeat list
   --------------------------------------------------------------------------
   The options on an attached group cannot be added or removed here — that
   belongs in the library, so every dish gets the new choice. What IS editable
   is each one's price and whether it is offered, so the row is four controls
   wide and never a card.
   -------------------------------------------------------------------------- */
.jt-option-grid { display: grid; gap: .4rem; }

.jt-option {
  display: grid; align-items: center; gap: .5rem;
  grid-template-columns: 1fr 7rem auto 8rem;
  padding: .5rem .6rem;
  border: 1px solid var(--jt-line);
  border-radius: 8px;
  background: var(--jt-surface-sunk);
}
.jt-option.is-off { opacity: .5; }

.jt-option-name {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  font-size: .88rem; color: var(--jt-ink);
}
.jt-option-price { font-variant-numeric: tabular-nums; }

.jt-option-flag {
  display: inline-flex; align-items: center; gap: .35rem;
  font-size: .8rem; color: var(--jt-body); cursor: pointer; white-space: nowrap;
}
.jt-option-flag input { margin: 0; }

.jt-option .invalid-feedback { grid-column: 1 / -1; }

@media (max-width: 40rem) {
  .jt-option { grid-template-columns: 1fr 1fr; }
  .jt-option-name { grid-column: 1 / -1; }
}

/* --------------------------------------------------------------------------
   `hidden` must actually hide
   --------------------------------------------------------------------------
   Bootstrap's display utilities are declared `!important`, so an element
   carrying `d-flex` stays visible with the `hidden` attribute set — and every
   conditional block in this dashboard uses that attribute. The failure is
   silent and looks like a JavaScript bug, so the rule lives here once rather
   than being worked around per block.

   `[hidden]` is the last word on visibility. Nothing should override it.
   -------------------------------------------------------------------------- */
[hidden] { display: none !important; }

/* ==========================================================================
   The basket
   ========================================================================== */
.jt-basket {
  position: fixed; inset-block-end: 1rem; inset-inline: 1rem;
  display: flex; align-items: center; gap: .6rem;
  padding: .85rem 1.1rem; border-radius: 999px;
  background: var(--jt-accent); color: var(--jt-accent-ink);
  text-decoration: none; font-weight: 600;
  box-shadow: var(--jt-shadow-lg); z-index: 40;
  /* Above the home indicator on a phone, so the last dish is reachable. */
  margin-block-end: env(safe-area-inset-bottom, 0);
}
.jt-basket:hover { background: var(--jt-accent-hover); color: var(--jt-accent-ink); }
.jt-basket-count {
  margin-inline-start: auto; min-inline-size: 1.6rem; text-align: center;
  background: color-mix(in oklab, var(--jt-accent-ink) 22%, transparent);
  border-radius: 999px; padding: .1rem .5rem; font-variant-numeric: tabular-nums;
}

.jt-cart-head { display: grid; gap: .3rem; margin-block-end: 1.25rem; }
.jt-cart-head h1 { margin: 0; font-size: 1.5rem; }

.jt-cart-lines { list-style: none; margin: 0; padding: 0; display: grid; gap: .75rem; }

.jt-cart-line {
  display: flex; gap: .8rem; align-items: flex-start;
  padding: .8rem; border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius); background: var(--jt-surface);
}
/* Greyed with a band, never hidden: the customer chose it, and it may come
   back at six. Deleting somebody's choice out from under them is worse. */
.jt-cart-line.is-blocked {
  opacity: .62;
  border-inline-start: 3px solid var(--jt-danger);
}

.jt-cart-photo {
  inline-size: 4rem; block-size: 4rem; flex: none;
  border-radius: 8px; overflow: hidden; background: var(--jt-surface-sunk);
  display: grid; place-items: center; color: var(--jt-muted);
}
.jt-cart-photo img { inline-size: 100%; block-size: 100%; object-fit: cover; }

.jt-cart-body { flex: 1; min-inline-size: 0; display: grid; gap: .3rem; }
.jt-cart-name { font-weight: 600; color: var(--jt-ink); }

.jt-cart-options { list-style: none; margin: 0; padding: 0; font-size: .82rem; color: var(--jt-muted); }
.jt-cart-options li { display: flex; gap: .35rem; align-items: baseline; }
.jt-delta { font-variant-numeric: tabular-nums; color: var(--jt-body); }
.jt-x { color: var(--jt-muted); }

.jt-cart-blocked { font-size: .8rem; color: var(--jt-danger); }

.jt-cart-note textarea {
  inline-size: 100%; font-size: .82rem; padding: .35rem .5rem;
  border: 1px dashed var(--jt-line-strong); border-radius: 6px;
  background: transparent; color: var(--jt-body); resize: vertical;
}

.jt-cart-side { display: grid; gap: .4rem; justify-items: end; flex: none; }
.jt-cart-total { font-weight: 600; font-variant-numeric: tabular-nums; }
.jt-cart-remove {
  border: 0; background: none; color: var(--jt-muted);
  font-size: .78rem; cursor: pointer; padding: .2rem;
}
.jt-cart-remove:hover { color: var(--jt-danger); }

.jt-stepper {
  display: inline-flex; align-items: center; gap: .2rem;
  border: 1px solid var(--jt-line-strong); border-radius: 999px; padding: .15rem;
}
.jt-stepper button {
  inline-size: 1.9rem; block-size: 1.9rem; border: 0; border-radius: 50%;
  background: var(--jt-surface-sunk); color: var(--jt-ink);
  font-size: 1.05rem; line-height: 1; cursor: pointer;
}
.jt-stepper button:hover { background: var(--jt-accent-soft); color: var(--jt-accent); }
.jt-stepper output { min-inline-size: 1.6rem; text-align: center; font-variant-numeric: tabular-nums; }
.jt-stepper input[type="number"] {
  inline-size: 3.4rem; border: 0; background: transparent; text-align: center;
  color: var(--jt-ink); font-variant-numeric: tabular-nums;
}

.jt-totals {
  margin-block-start: 1.25rem; padding: 1rem;
  border: 1px solid var(--jt-line); border-radius: var(--jt-radius);
  background: var(--jt-surface); display: grid; gap: .45rem;
}
.jt-totals > div { display: flex; justify-content: space-between; gap: 1rem; font-size: .9rem; }
.jt-totals span:last-child { font-variant-numeric: tabular-nums; }
.jt-tax { color: var(--jt-muted); font-size: .82rem !important; }
.jt-grand {
  margin-block-start: .35rem; padding-block-start: .6rem;
  border-block-start: 1px solid var(--jt-line);
  font-weight: 700; font-size: 1.05rem !important;
}

.jt-cart-actions {
  display: grid; gap: .6rem; justify-items: stretch;
  margin-block: 1.25rem 5rem;
}
.jt-checkout { inline-size: 100%; }
.jt-cart-actions form { justify-self: center; }

.jt-note {
  padding: .6rem .8rem; border-radius: var(--jt-radius);
  background: var(--jt-warn-soft); color: var(--jt-warn);
  font-size: .85rem; margin-block-end: 1rem;
}

/* ==========================================================================
   The item sheet
   ========================================================================== */
.jt-sheet {
  border: 0; padding: 0; border-radius: var(--jt-radius-lg) var(--jt-radius-lg) 0 0;
  inline-size: min(34rem, 100%); max-block-size: 92vh;
  background: var(--jt-surface); color: var(--jt-ink);
  margin-inline: auto; margin-block-end: 0;
}
.jt-sheet::backdrop { background: rgb(0 0 0 / .55); }
.jt-sheet form { display: flex; flex-direction: column; max-block-size: 92vh; }

.jt-sheet-head {
  display: flex; align-items: center; gap: .75rem;
  padding: 1rem 1.1rem; border-block-end: 1px solid var(--jt-line);
}
.jt-sheet-head h2 { margin: 0; font-size: 1.15rem; flex: 1; }
.jt-sheet-close {
  border: 0; background: var(--jt-surface-sunk); color: var(--jt-body);
  inline-size: 2rem; block-size: 2rem; border-radius: 50%; font-size: 1.2rem;
  line-height: 1; cursor: pointer;
}

.jt-sheet-body { padding: 1rem 1.1rem; overflow-y: auto; flex: 1; display: grid; gap: 1.25rem; }
.jt-sheet-desc { color: var(--jt-body); margin: 0; font-size: .9rem; line-height: 1.7; }
.jt-sheet-hint { font-size: .78rem; color: var(--jt-muted); font-weight: 400; }

.jt-sheet-group { border: 0; padding: 0; margin: 0; display: grid; gap: .4rem; }
.jt-sheet-group legend {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  font-weight: 600; font-size: .95rem; margin-block-end: .35rem; padding: 0;
}

.jt-choice {
  display: flex; align-items: center; gap: .6rem;
  padding: .6rem .7rem; border: 1px solid var(--jt-line);
  border-radius: 10px; cursor: pointer; background: var(--jt-surface);
}
.jt-choice:has(input:checked) { border-color: var(--jt-accent); background: var(--jt-accent-soft); }
.jt-choice.is-off { opacity: .5; cursor: not-allowed; }
.jt-choice-name { flex: 1; font-size: .9rem; }

.jt-ingredient {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding-block: .2rem;
}
.jt-ingredient .jt-choice { flex: 1; min-inline-size: 12rem; }
.jt-ingredient.is-off { opacity: .5; }
.jt-ingredient-text { margin: 0; font-size: .88rem; color: var(--jt-body); padding: .35rem 0; }

.jt-extra {
  display: inline-flex; align-items: center; gap: .45rem;
  font-size: .82rem; color: var(--jt-body);
}
.jt-extra input {
  inline-size: 3.6rem; text-align: center;
  border: 1px solid var(--jt-line-strong); border-radius: 8px;
  padding: .3rem; background: var(--jt-surface); color: var(--jt-ink);
  font-variant-numeric: tabular-nums;
}

.jt-sheet-note { display: grid; gap: .3rem; font-size: .85rem; color: var(--jt-body); }
.jt-sheet-note textarea {
  inline-size: 100%; padding: .5rem .6rem; border-radius: 8px;
  border: 1px solid var(--jt-line-strong);
  background: var(--jt-surface); color: var(--jt-ink); resize: vertical;
}

.jt-sheet-foot {
  display: flex; align-items: center; gap: .75rem;
  padding: .9rem 1.1rem; border-block-start: 1px solid var(--jt-line);
  background: var(--jt-surface);
}
.jt-sheet-foot .btn { flex: 1; }

.jt-add { inline-size: 100%; margin-block-start: 1.25rem; }
.jt-add-form { margin-block-start: 1.25rem; }

@media (min-width: 34rem) {
  .jt-sheet { border-radius: var(--jt-radius-lg); margin-block: auto; }
  .jt-basket { inset-inline: auto; inset-inline-end: 1.5rem; }
}

/* The add control on a dish card.
   A sibling of the card link rather than a child: a <form> cannot live inside
   an <a>, and nesting the two would make every tap ambiguous. */
.jt-dish-cell { position: relative; display: grid; }
.jt-dish-add-form { position: absolute; inset-block-end: .6rem; inset-inline-end: .6rem; margin: 0; }
.jt-dish-add {
  position: absolute; inset-block-end: .6rem; inset-inline-end: .6rem;
  inline-size: 2.1rem; block-size: 2.1rem; border: 0; border-radius: 50%;
  background: var(--jt-accent); color: var(--jt-accent-ink);
  font-size: 1.15rem; line-height: 1; cursor: pointer;
  display: grid; place-items: center; text-decoration: none;
  box-shadow: var(--jt-shadow-sm);
}
.jt-dish-add-form .jt-dish-add { position: static; }
.jt-dish-add:hover { background: var(--jt-accent-hover); color: var(--jt-accent-ink); }
.jt-dish-cell .jt-dish { flex: 1; padding-inline-end: 3.2rem; }

/* The drag handle on a reorderable row. */
.jt-drag {
  cursor: grab; color: var(--jt-muted); background: none; border: 0;
  padding: .35rem; border-radius: 6px; line-height: 1;
}
.jt-drag:hover, .jt-drag:focus-visible { color: var(--jt-accent); background: var(--jt-accent-soft); }
.jt-drag:active { cursor: grabbing; }
tr.is-dragging { opacity: .45; }

/* ==========================================================================
   Checkout, and the confirmation
   ========================================================================== */
.jt-checkout-form { display: grid; gap: 1rem; margin-block: 1.25rem 5rem; }

.jt-field { display: grid; gap: .3rem; font-size: .88rem; color: var(--jt-body); }
.jt-field input, .jt-field select, .jt-field textarea {
  inline-size: 100%; padding: .65rem .75rem; border-radius: 10px;
  border: 1px solid var(--jt-line-strong);
  background: var(--jt-surface); color: var(--jt-ink);
  font-size: 1rem; /* 16px: anything smaller makes iOS zoom on focus */
}
.jt-field input:focus, .jt-field select:focus, .jt-field textarea:focus {
  outline: 2px solid var(--jt-accent); outline-offset: 1px; border-color: var(--jt-accent);
}
.jt-field small { color: var(--jt-muted); font-size: .76rem; }

.jt-done { display: grid; gap: 1rem; justify-items: center; text-align: center; margin-block: 2rem 5rem; }
.jt-done h1 { margin: 0; font-size: 1.5rem; }
.jt-done-tick {
  inline-size: 3.5rem; block-size: 3.5rem; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--jt-ok-soft); color: var(--jt-ok);
}
.jt-done-number { display: grid; gap: .2rem; margin: 0; color: var(--jt-muted); font-size: .85rem; }
.jt-done-number strong { font-size: 2.2rem; color: var(--jt-ink); font-variant-numeric: tabular-nums; }
.jt-done-eta { margin: 0; color: var(--jt-body); }
.jt-done-hint { margin: 0; color: var(--jt-muted); font-size: .82rem; max-inline-size: 24rem; }
.jt-done .jt-totals { inline-size: 100%; text-align: start; }
.jt-done-options { color: var(--jt-muted); font-size: .8rem !important; }
.jt-done-options small { color: var(--jt-muted); }

/* ==========================================================================
   The orders screen
   ========================================================================== */
.jt-order-seq {
  font-size: 1.25rem; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--jt-ink); text-decoration: none;
}
.jt-order-seq:hover { color: var(--jt-accent); }
.jt-num { font-variant-numeric: tabular-nums; }

.jt-order-grid { display: grid; gap: 1rem; grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr); }
@media (max-width: 60rem) { .jt-order-grid { grid-template-columns: 1fr; } }

.jt-order-line {
  display: flex; gap: .75rem; align-items: flex-start; flex-wrap: wrap;
  padding-block: .75rem;
  border-block-end: 1px solid var(--jt-line);
}
.jt-order-line:last-of-type { border-block-end: 0; }
/* Struck through, never removed: somebody rang it up and that happened. */
.jt-order-line.is-void { opacity: .55; }
.jt-order-line.is-void .jt-order-name { text-decoration: line-through; }

.jt-order-qty { font-weight: 700; font-variant-numeric: tabular-nums; min-inline-size: 2rem; }
.jt-order-body { flex: 1; min-inline-size: 0; display: grid; gap: .2rem; justify-items: start; }
.jt-order-name { font-weight: 600; color: var(--jt-ink); }
.jt-order-total { font-variant-numeric: tabular-nums; font-weight: 600; }

.jt-order-options { list-style: none; margin: 0; padding: 0; font-size: .82rem; color: var(--jt-muted); }
.jt-order-options li { display: flex; gap: .35rem; align-items: baseline; }

/* A note the kitchen misses is a plate that comes back. */
.jt-order-note {
  margin: .2rem 0 0; padding: .3rem .5rem; border-radius: 6px;
  background: var(--jt-warn-soft); color: var(--jt-warn);
  font-size: .84rem; font-weight: 600;
}

.jt-order-void, .jt-order-cancel { inline-size: 100%; }
.jt-order-void summary, .jt-order-cancel summary { list-style: none; display: inline-block; cursor: pointer; }
.jt-order-void summary::-webkit-details-marker,
.jt-order-cancel summary::-webkit-details-marker { display: none; }

.jt-order-actions { display: grid; gap: .6rem; }
.jt-order-actions form { margin: 0; }

.jt-timeline { list-style: none; margin: 0; padding: 0; display: grid; gap: .55rem; }
.jt-timeline li { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; font-size: .88rem; }
.jt-timeline-time {
  font-variant-numeric: tabular-nums; color: var(--jt-muted);
  min-inline-size: 3rem; font-size: .82rem;
}
.jt-timeline-what { color: var(--jt-body); }

.jt-chip-danger { background: var(--jt-danger-soft); color: var(--jt-danger); border-color: transparent; }

/* A tab that exists but is not reachable yet — shown rather than hidden, so a
   feature somebody is looking for does not appear to be missing. */
.jt-tab.is-disabled { opacity: .4; cursor: not-allowed; }

/* The customer's own view of their order. */
.jt-whatsapp { inline-size: 100%; }

.jt-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; inline-size: 100%; max-inline-size: 20rem; }
.jt-steps li { display: flex; align-items: center; gap: .6rem; color: var(--jt-muted); font-size: .9rem; }
.jt-step-dot {
  inline-size: .8rem; block-size: .8rem; border-radius: 50%; flex: none;
  border: 2px solid var(--jt-line-strong); background: transparent;
}
.jt-steps li.is-done { color: var(--jt-body); }
.jt-steps li.is-done .jt-step-dot { background: var(--jt-ok); border-color: var(--jt-ok); }
.jt-steps li.is-now { color: var(--jt-ink); font-weight: 600; }
.jt-steps li.is-now .jt-step-dot { background: var(--jt-accent); border-color: var(--jt-accent); }

.jt-done-contact { display: flex; gap: .5rem; flex-wrap: wrap; justify-content: center; }

/* --------------------------------------------------------------------------
   The Publishing panel — #f-availability-scheduled-publish
   --------------------------------------------------------------------------
   The radios reuse .jt-mode from the availability picker deliberately: the two
   cards sit within a screen of each other on the same editor, they ask the
   same shape of question — pick one of these — and two different-looking
   answers to the same shape of question is how a form starts feeling assembled
   rather than designed.

   What is new here is the strip above them. A status word alone does not
   answer "has anybody ever seen this?", and that is the question somebody
   actually has when they are looking at a dish that is back in draft.
   -------------------------------------------------------------------------- */
.jt-publish-now {
  display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
  margin-block-end: 1rem; padding-block-end: .9rem;
  border-block-end: 1px solid var(--jt-line);
}
.jt-publish-facts { font-size: .82rem; color: var(--jt-muted); line-height: 1.6; }
.jt-publish-event { color: var(--jt-body); }

.jt-publish-when { display: grid; gap: .25rem; }
.jt-publish-when .mb-3 { margin-block-end: .5rem; }
.jt-publish-when input[type="datetime-local"] { font-variant-numeric: tabular-nums; }

/* The seasonal-window note, which is a caution rather than an error: the two
   mechanisms are orthogonal and a dish can be invisible for either reason. */
.jt-publish-clash { color: var(--jt-warn); }

/* The table bar. Persistent while a scan is live, and quiet about it: this
   sits above the menu on every page, so it earns its space by being one line
   and giving way to the food. */
.jt-table-bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .75rem;
    margin-block-end: 1rem;
    border: 1px solid var(--jt-line);
    border-radius: .6rem;
    background: color-mix(in srgb, var(--jt-accent) 8%, transparent);
    font-size: .85rem;
}
.jt-table-bar svg { flex: none; color: var(--jt-accent); }
.jt-table-bar form { margin: 0; margin-inline-start: auto; }
.jt-table-bar button {
    border: 0;
    background: none;
    padding: 0;
    color: var(--jt-accent);
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}
.jt-locked {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: 0;
    padding: .55rem .7rem;
    border: 1px solid var(--jt-line);
    border-radius: .5rem;
    background: var(--jt-surface, transparent);
}

/* Twenty-four bars, drawn with divs. A chart library to render twenty-four
   numbers costs more than it draws. */
.jt-hours { display: flex; align-items: flex-end; gap: 2px; height: 120px; }
.jt-hour { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; }
.jt-hour span { display: block; width: 100%; background: var(--jt-accent); border-radius: 2px 2px 0 0; }
.jt-hour small { font-size: .6rem; color: var(--jt-muted); }

/* The media grid. Square tiles, because a picture library scanned by eye is
   scanned by shape, and ragged heights make that slower. */
.jt-media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: .75rem; }
.jt-media-tile {
    position: relative; display: block; text-decoration: none; color: inherit;
    border: 1px solid var(--jt-line); border-radius: .5rem; overflow: hidden; background: var(--jt-surface, transparent);
}
.jt-media-tile img { width: 100%; aspect-ratio: 1; object-fit: cover; display: block; }
.jt-media-tile small { display: block; padding: .35rem .5rem; font-size: .7rem; color: var(--jt-muted); }
.jt-media-uses {
    position: absolute; inset-block-start: .35rem; inset-inline-end: .35rem;
    min-width: 1.4rem; text-align: center; padding: .1rem .35rem; border-radius: 1rem;
    font-size: .7rem; background: var(--jt-accent); color: #fff;
}
/* Nothing is using it — the one a person clearing out is looking for. */
.jt-media-uses.is-free { background: var(--jt-muted); }

/* Plan limits. The bar is the point: a number on its own does not say how
   close somebody is to their ceiling. */
.jt-limit { margin-block-end: .75rem; }
.jt-limit-head { display: flex; justify-content: space-between; gap: 1rem; align-items: baseline; font-size: .85rem; padding-block: .2rem; }
.jt-bar { height: 6px; border-radius: 3px; background: var(--jt-line); overflow: hidden; }
.jt-bar span { display: block; height: 100%; background: var(--jt-accent); }
.jt-bar span.is-full { background: #b00; }

/* ---------------------------------------------------------------------------
 * Home tiles — the landing grid
 *
 * Cards in one column, icons four across. Both are `home_tiles` rows; the CSS
 * knows only about `size`, so adding a kind never touches this file.
 * ------------------------------------------------------------------------ */

.jt-tiles { display: grid; gap: .75rem; margin-block-end: 1.25rem; }

.jt-tile {
  position: relative;
  display: block;
  min-height: 7rem;
  border-radius: var(--jt-radius-lg);
  overflow: hidden;
  background: var(--jt-surface-sunk);
  border: 1px solid var(--jt-line);
  color: var(--jt-ink);
  text-decoration: none;
}
.jt-tile:hover { border-color: var(--jt-accent); text-decoration: none; }
.jt-tile:focus-visible { outline: 2px solid var(--jt-accent); outline-offset: 2px; }

.jt-tile-bg {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}

/* A solid plate rather than a gradient scrim: a gradient over an unpredictable
   photograph gives an unpredictable contrast ratio, and the label has to be
   readable on every image a restaurant uploads, not on the ones we tested. */
.jt-tile-label {
  position: relative;
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  margin: 4.25rem .75rem .75rem;
  padding: .5rem .75rem;
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  box-shadow: 0 1px 2px rgb(0 0 0 / .08);
}
.jt-tile-label b { font-size: 1.02rem; color: var(--jt-ink); }
.jt-tile-label > span { color: var(--jt-muted); font-size: .82rem; }
.jt-tile:not(:has(.jt-tile-bg)) .jt-tile-label { margin-block-start: .75rem; }

.jt-tile-icons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: .5rem;
  margin-block-end: 1.5rem;
}
.jt-tile-icon {
  display: grid; justify-items: center; align-content: start; gap: .35rem;
  padding: .7rem .25rem;
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  color: var(--jt-ink);
  font-size: .68rem;
  line-height: 1.3;
  text-align: center;
  text-decoration: none;
}
.jt-tile-icon:hover { border-color: var(--jt-accent); text-decoration: none; }
.jt-tile-icon span { color: var(--jt-muted); }

/* WiFi, inside its tile. Never rendered for a network with is_public = 0. */
.jt-wifi { position: relative; display: grid; gap: .5rem; margin: 0 .75rem .75rem; }
.jt-wifi-row {
  display: grid; grid-template-columns: 1fr auto; gap: .75rem; align-items: center;
  padding: .6rem .75rem;
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
}
.jt-wifi-facts { display: grid; gap: .25rem; }
.jt-wifi-facts > span { display: flex; gap: .5rem; align-items: baseline; }
.jt-wifi-facts small { color: var(--jt-muted); font-size: .72rem; min-width: 4.5rem; }
.jt-wifi-facts code { font-size: .92rem; color: var(--jt-ink); }
.jt-wifi-qr { display: block; width: 5rem; }
.jt-wifi-qr svg { width: 100%; height: auto; display: block; }

/* ============================================================ PHOTO WALL */

/*
    Content → Photo wall. #f-marketing-photo-wall

    A queue drawn as a grid, because the decision is made by looking at the
    photograph. Everything else on a tile is there to make that decision
    faster: who sent it, when, from where, and the two buttons.

    The tile is focusable. Keyboard moderation is the actual job on a holiday
    Monday, so the thing the arrow keys move between has to be a real focus
    target with a real focus ring — an outline that only appears on hover is
    an outline nobody moderating with a keyboard ever sees.
*/

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

.jt-filters .form-label { font-size: .82rem; margin-block-end: .25rem; }

.jt-wall-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: .9rem;
  margin-block-end: 1.25rem;
}

.jt-wall-tile {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  overflow: hidden;
}
.jt-wall-tile:focus-visible {
  outline: 2px solid var(--jt-accent);
  outline-offset: 2px;
}
.jt-wall-tile.is-featured { border-color: var(--jt-accent); }

.jt-wall-shot { display: block; aspect-ratio: 4 / 3; background: var(--jt-surface-sunk); }
.jt-wall-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The checkbox sits ON the photograph, in a chip, so it is legible over a
   bright sky as well as a dark table. */
.jt-wall-pick {
  position: absolute;
  inset-block-start: .5rem;
  inset-inline-start: .5rem;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 28px; height: 28px;
  border-radius: 8px;
  background: rgba(255, 255, 255, .88);
  box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
  cursor: pointer;
}

.jt-wall-meta {
  display: flex;
  flex-wrap: wrap;
  gap: .35rem;
  align-items: center;
  padding: .6rem .7rem .35rem;
  font-size: .82rem;
}
.jt-wall-meta b { flex-basis: 100%; font-size: .88rem; }

.jt-wall-actions {
  display: flex;
  gap: .35rem;
  flex-wrap: wrap;
  padding: .35rem .7rem .7rem;
  margin-block-start: auto;
}

.jt-wall-full {
  display: block;
  width: 100%;
  max-height: 60vh;
  object-fit: contain;
  border-radius: var(--jt-radius);
  background: var(--jt-surface-sunk);
}

/* showModal() brings the focus trap, Escape and the inert background; this is
   only what it looks like. */
.jt-dialog {
  width: min(94vw, 520px);
  padding: 1.25rem;
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  color: var(--jt-ink);
}
.jt-dialog::backdrop { background: rgba(0, 0, 0, .45); }
.jt-dialog h2 { margin-block: 0 1rem; font-size: 1.05rem; }

/* Row actions on a list screen. Publish, edit and delete belong where the
   operator is looking — a draft whose only route to publication is two clicks
   away behind a screen they just left is a draft that stays a draft. */
.jt-col-actions { width: 1%; white-space: nowrap; }
.jt-row-actions {
  display: flex; gap: .35rem; align-items: center; justify-content: flex-end;
}
.jt-row-actions form { margin: 0; }

/* ============================================================== THEMES */

/*
    Appearance → Themes. The card is mostly picture, because a theme is
    chosen by looking at it — everything else on the card exists to answer
    "may I have this one, and is it the one I am using".
*/
.jt-theme-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
  margin-block-end: 1.25rem;
}
.jt-theme {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--jt-line);
  border-radius: var(--jt-radius);
  background: var(--jt-surface);
  overflow: hidden;
}
.jt-theme.is-active { border-color: var(--jt-accent); box-shadow: 0 0 0 1px var(--jt-accent); }

/* Owned but not deployed here, and greyed rather than hidden: the catalogue
   is where the roadmap lives and the restaurant should see what is coming. */
.jt-theme.is-pending .jt-theme-shot,
.jt-theme.is-locked .jt-theme-shot { opacity: .5; filter: grayscale(1); }

.jt-theme-shot {
  aspect-ratio: 16 / 10;
  display: grid;
  place-items: center;
  background: var(--jt-surface-sunk);
  color: var(--jt-muted);
}
.jt-theme-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.jt-theme-body { padding: .75rem .85rem .35rem; display: grid; gap: .35rem; }
.jt-theme-body h2 { margin: 0; font-size: 1rem; }
.jt-theme-body p { margin: 0; font-size: .84rem; color: var(--jt-muted); }
.jt-theme-tags { display: flex; flex-wrap: wrap; gap: .3rem; }

.jt-theme-foot { padding: .5rem .85rem .85rem; margin-block-start: auto; }

/* Theme options: one field per manifest entry, so the grid has to hold
   whatever a theme declares rather than a layout drawn for five knobs. */
.jt-option-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem;
}
.jt-option { display: grid; gap: .35rem; align-content: start; }
.jt-color-row { display: flex; gap: .5rem; align-items: center; }
.jt-color-row input[type="color"] {
  inline-size: 3rem; block-size: 2.4rem; padding: .15rem;
  border: 1px solid var(--jt-line); border-radius: .5rem; background: var(--jt-surface);
  cursor: pointer; flex: none;
}
.jt-theme-foot { display: flex; gap: .35rem; flex-wrap: wrap; }

/* A value somebody has to copy exactly — a DNS record, a token. Monospace and
   selectable, wrapping rather than scrolling, because it is going into another
   system's form and a truncated copy is a support call. */
.jt-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .82rem;
  background: var(--jt-surface-sunk);
  border: 1px solid var(--jt-line);
  border-radius: .4rem;
  padding: .5rem .6rem;
  user-select: all;
}

/* A secret's row: what is known about it on one side, the way to replace it
   on the other. Wraps rather than compressing the input, because a credential
   pasted into a 60px box is a credential nobody can check. */
.jt-secret-row {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  align-items: flex-start;
  justify-content: space-between;
  padding: .85rem 0;
  border-top: 1px solid var(--jt-line);
}
.jt-secret-row:first-of-type { border-top: 0; }
.jt-secret-form { display: flex; gap: .5rem; flex: 1 1 18rem; max-width: 26rem; }
.jt-code-inline { display: inline; padding: .1rem .35rem; border-radius: .25rem; }

/* The ability checkboxes: many short labels, so a responsive grid rather than
   a column forty rows long. */
.jt-ability-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: .35rem .9rem;
}
.jt-choice { display: flex; gap: .6rem; align-items: flex-start; padding: .45rem 0; }
.jt-choice > span { display: flex; flex-direction: column; gap: .1rem; }
.jt-choice-inline { display: flex; gap: .45rem; align-items: baseline; }

/* A language the plan does not carry: visible, legible, and plainly not a
   choice. Not display:none — this is the only place a restaurant learns the
   language exists. */
.jt-lang-option.is-locked {
  opacity: .5;
  cursor: not-allowed;
  border-style: dashed;
}

/* A day-by-day bar chart, in CSS. One series of at most 365 values does not
   justify a charting library — which here would mean a build step, a CDN or a
   vendored megabyte, all three ruled out. */
.jt-bars {
  display: flex; align-items: flex-end; gap: 2px;
  height: 9rem; padding-top: .5rem;
  overflow-x: auto;
}
.jt-bar {
  flex: 1 0 3px; min-width: 3px;
  height: var(--h, 0%);
  background: var(--jt-accent);
  border-radius: 2px 2px 0 0;
  /* A day with no views is still a day: a hairline keeps the axis readable
     rather than leaving a gap that reads as missing data. */
  min-height: 1px;
}
.jt-bar:hover { background: var(--jt-accent-hover); }

/* Screen-reader-only, for the bar chart's per-day values. Bootstrap ships
   this class but the public menu does not load Bootstrap. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}

/* The dashboard front door: cards that are whole links, so the target is the
   card and not a word inside it. */
.jt-home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: 1rem;
}

.jt-home-card {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--jt-line);
    border-radius: var(--jt-radius, .6rem);
    background: var(--jt-surface, #fff);
    color: inherit;
    text-decoration: none;
    transition: border-color .15s ease;
}

.jt-home-card:hover,
.jt-home-card:focus-visible {
    border-color: var(--jt-accent, #888);
}

.jt-home-figure {
    font-size: 2.4rem;
    font-weight: 700;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.jt-home-line {
    margin-top: .35rem;
    font-variant-numeric: tabular-nums;
}
